﻿// 将GLFW库的定义包含进来，而GLFW库会自动包含Vulkan库的头文件
#define GLFW_INCLUDE_VULKAN
#include <glfw/glfw3.h>

// GLM_FORCE_RADIANS用于启用GLM中的常量定义，用于指定角度的单位为弧度
#define GLM_FORCE_RADIANS
// GLM_FORCE_DEFAULT_ALIGNED_GENTYPES用于控制GLM库中数据类型的内存对齐行为
// #define GLM_FORCE_DEFAULT_ALIGNED_GENTYPES
// GLM_FORCE_DEPTH_ZERO_TO_ONE是GLM中定义的宏，用来控制GLM在处理深度值是的范围映射，使范围时[0, 1]
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
#include <glm/glm.hpp>
// 用于与矩阵变换相关的功能
#include <glm/gtc/matrix_transform.hpp>

// GLM_ENABLE_EXPERIMENTAL是GLM库，用于启用GLM库的实验性功能
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/gtx/hash.hpp>

// STB_IMAGE_IMPLEMENTATION用来避免多次导入并定义函数实现
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>

// TINYOBJLOADER_IMPLEMENTATION是用于指示你正在实现TingOBJLoader库的功能
#define TINYOBJLOADER_IMPLEMENTATION
#include <tiny_obj_loader.h>

#include <iostream>
#include <stdexcept>
#include <functional>
#include <cstdlib>
#include <chrono>
#include <vector>
#include <optional>
#include <set>
#include <algorithm>
#include <fstream>
#include <array>

const uint32_t WIDTH = 800;
const uint32_t HEIGHT = 600;

const std::string MODEL_PATH = "models/viking_room.obj";
const std::string TEXTURE_PATH = "textures/viking_room.png";

const int MAX_FRAMES_IN_FLIGHT = 2;

const std::vector<const char*> validationLayers = {
	"VK_LAYER_KHRONOS_validation"
};

const std::vector<const char*> deviceExtensions = {
	VK_KHR_SWAPCHAIN_EXTENSION_NAME
};

struct QueueFamilyIndices {
	// std::optional 是 C++17 引入的一个模板类，用于表示一个值可能存在，也可能不存在的情况，
	// 通过调用has_value()成员函数来查询它是否包含值
	std::optional<uint32_t> graphicsFamily;
	std::optional<uint32_t> presentFamily;

	bool isComplete() {
		return graphicsFamily.has_value() && presentFamily.has_value();
	}
};

// VSync（Vertical Synchronization，垂直同步）是一个显示技术概念，核心作用是让GPU渲染帧与显示器刷新周期同步，防止画面撕裂（Tearing）
struct SwapChainSupportDetails {
	// VkSurfaceCapabilitiesKHR用来描述物理设备针对某个Surface的能力
	VkSurfaceCapabilitiesKHR capabilities;
	// VkSurfaceFormatKHR用来描述某个物理设备在指定Surface上支持的像素格式和颜色空间的组合
	std::vector<VkSurfaceFormatKHR> formats;
	// VkPresentModeKHR用来描述Swapchain图像呈现（显示）给Surface时采用的策略
	std::vector<VkPresentModeKHR> presentModes;
};

VkResult CreateDebugUtilsMessengerEXT(
	VkInstance instance, 
	const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, 
	const VkAllocationCallbacks* pAllocator, 
	VkDebugUtilsMessengerEXT* pDebugMessenger) {
	// PFN_vkCreateDebugUtilsMessengerEXT是函数指针类型定义，指向vkCreateDebugUtilsMessengerEXT，
	// vkGetInstanceProcAddr用来动态获取Vulkan函数的入口地址
	auto func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT");
	if (func != nullptr) {
		return func(instance, pCreateInfo, pAllocator, pDebugMessenger);
	}
	else {
		return VK_ERROR_EXTENSION_NOT_PRESENT;
	}
}

void DestroyDebugUtilsMessengerEXT(
	VkInstance instance, 
	VkDebugUtilsMessengerEXT debugMessager, 
	const VkAllocationCallbacks* pAllocator) {
	auto func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT");
	if (func != nullptr) {
		func(instance, debugMessager, pAllocator);
	}
}

struct Vertex {
	// glm::vec2 pos;
	glm::vec3 pos;
	glm::vec3 color;
	glm::vec2 texCoord;
	
	// VkVertexInputBindingDescription是用于描述顶点输入绑定的结构体
	// binding 指定绑定点的索引，绑定点是一个整数，指示顶点输入数据的槽位
	// stride 每个顶点的字节大小，也就是从一个顶点数据的开始到下一个顶点数据的开始的偏移量
	// inputRate 指定每次顶点数据的传输方式，VK_VERTEX_INPUT_RATE_VERTEX（每次为每个顶点读取数据）、VK_VERTEX_INPUT_RATE_INSTANCE（每次为每个实例读取数据）
	static VkVertexInputBindingDescription getBindingDescription() {
		VkVertexInputBindingDescription bindingDescription{};
		bindingDescription.binding = 0;
		bindingDescription.stride = sizeof(Vertex);
		bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
		return bindingDescription;
	}
	
	// VkVertexInputAttributeDescription适用于描述顶点输入属性的结构体
	// binding 指定这个属性绑定到哪个顶点输入绑定点（VkVertexInputBindingDescription中定义的binding）
	// location 指定属性的位置，对应着着色器输入变量的location
	// format 属性的数据格式
	// offset 属性在顶点数据中的偏移量
	// offsetof是C/C++中的宏，用于获取结构体成员相对于结构体起始位置的偏移量
	static std::array<VkVertexInputAttributeDescription, 3> getAttributeDescriptions() {
		std::array<VkVertexInputAttributeDescription, 3> attributeDescriptions{};
		attributeDescriptions[0].binding = 0;
		attributeDescriptions[0].location = 0;
		attributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT;
		attributeDescriptions[0].offset = offsetof(Vertex, pos);

		attributeDescriptions[1].binding = 0;
		attributeDescriptions[1].location = 1;
		attributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT;
		attributeDescriptions[1].offset = offsetof(Vertex, color);

		attributeDescriptions[2].binding = 0;
		attributeDescriptions[2].location = 2;
		attributeDescriptions[2].format = VK_FORMAT_R32G32_SFLOAT;
		attributeDescriptions[2].offset = offsetof(Vertex, texCoord);
		return attributeDescriptions;
	}

	bool operator==(const Vertex& other) const {
		return pos == other.pos && color == other.color && texCoord == other.texCoord;
	}
};

namespace std {
	template<> struct hash<Vertex> {
		size_t operator()(Vertex const& vertex) const {
			return ((hash<glm::vec3>()(vertex.pos) ^ (hash<glm::vec3>()(vertex.color) << 1)) >> 1) ^ (hash<glm::vec2>()(vertex.texCoord) << 1);
		}
	};
}

struct UniformBufferObject {
	// glm::vec2 foo;
	// glm::mat4是GLM库中最常用的4x4矩阵类型广泛用于3D图形编程中的变换矩阵
	// alignas(16) glm::mat4 model;
	alignas(16) glm::mat4 model;
	alignas(16) glm::mat4 view;
	alignas(16) glm::mat4 proj;
};

// NDEBUG宏是C++标准的一部分，表示“不调试”
#ifdef NDEBUG
	const bool enableValidationLayers = false;
#else
	const bool enableValidationLayers = true;
#endif

class HelloTriangleApplication {
public:
	void run() {
		initWindow();
		initVulkan();
		mainLoop();
		cleanup();
	}

 private:
	 // GLFW窗口
	 GLFWwindow* window;
	 // VkInstance实例
	 // VkInstance是Vulkan API的顶层对象，它代表了应用程序和Vulkan库之间的连接。所有Vulkan操作都必须
	 // 基于一个有效的VkInstance才能进行
	 VkInstance instance;
	 // VkDebugUtilsMessengerEXT是调试回调对象的句柄
	 VkDebugUtilsMessengerEXT debugMessenger;

	 // VkDevice是Vulkan中的核心对象类型，中文翻译为逻辑设备
	 VkDevice device;

	 // VkPhysicalDevice表示物理GPU（显卡）设备
	 // VK_NULL_HANDLE是Vulkan常量，表示无效的句柄（HANDLE）
	 VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;

	 // VkSampleCountFlags用于表示图像或渲染目标的采样数的标志类型，通常用于多重采样或抗锯齿操作
	 VkSampleCountFlagBits msaaSamples = VK_SAMPLE_COUNT_1_BIT;

	 // VkQueue是用来执行命令的队列对象，是实际提交命令缓冲（VkCommandBuffer）给GPU执行的接口，
	 // 当设备被销毁时，设备队列会被隐式清理
	 VkQueue graphicsQueue;

	 VkQueue presentQueue;

	 // VkSurfaceKHR表示一个“渲染目标表面”的对象，也是最终图像要显示到的窗口或屏幕的抽象
	 VkSurfaceKHR surface;
		
	 // VkSwapchainKHR是交换链句柄，是对窗口输出图像队列的管理对象
	 VkSwapchainKHR swapChain;

	 // VkImage是图像资源句柄，表示GPU内存中某块二维/三维图像数据的抽象
	 std::vector<VkImage> swapChainImages;

	 VkFormat swapChainImageFormat;
	 VkExtent2D swapChainExtent;

	 // VkImageView是图像视图对象，描述如何访问VkImage的句柄
	 std::vector<VkImageView> swapChainImageViews;

	 // VkFramebuffer用来绑定渲染过程中使用的目标图像
	 std::vector<VkFramebuffer> swapChainFramebuffers;
		
	 // VkRenderPass定义了整个渲染过程的逻辑结构，告诉 GPU：要画几步、每步输出到哪里、哪些内容保留、哪些清除
	 VkRenderPass renderPass;
		
	 // VkDescriptorSetLayout是资源绑定系统的核心对象之一，表示一组描述符的布局定义
	 VkDescriptorSetLayout descriptorSetLayout;

	 // VkPipelineLayout是用于描述和管理着色器阶段（Shader Stage）使用资源绑定信息的结构体，
	 // 定义了管线（Pipeline）中所有资源的布局，包括统一变量（Uniforms）、存储缓冲区（Storage Buffers）、采样器（Samplers）、图像（Images）等
	 VkPipelineLayout pipelineLayout;

	 // VkPipeline表示图像或计算管线的对象
	 VkPipeline graphicsPipeline;


	 // VkCommandPool是用于管理命令缓冲区（VkCommandBuffer）的对象
	 VkCommandPool commandPool;

	 VkImage colorImage;

	 VkDeviceMemory colorImageMemory;

	 VkImageView colorImageView;

	 VkImage depthImage;

	 VkDeviceMemory depthImageMemory;

	 VkImageView depthImageView;

	 uint32_t mipLevels;

	 VkImage textureImage;

	 VkDeviceMemory textureImageMemory;

	 VkImageView textureImageView;

	 // VkSampler用于描述纹理采样器的对象
	 VkSampler textureSampler;

	 // VkCommandBuffer用于记录渲染、计算或其他GPU操作的对象
	 std::vector<VkCommandBuffer> commandBuffers;

	 // VkSemaphore是一种同步对象，用于协同不同操作之间的顺序
	 std::vector<VkSemaphore> imageAvailableSemaphores;
	 std::vector<VkSemaphore> renderFinishedSemaphores;
	 // VkFence是一种同步对象，用于在CPU和GPU之间进行同步
	 std::vector<VkFence> inFlightFences;

	 bool framebufferResized = false;

	 uint32_t currentFrame = 0;

	 std::vector<Vertex> vertices;
	 std::vector<uint32_t> indices;
	 
	 // VkBuffer用于表示一个内存缓冲区
	 VkBuffer vertexBuffer;

	 // VkDeviceMemory表示设备内存的对象类型，是一个句柄
	 VkDeviceMemory vertexBufferMemory;

	 VkBuffer indexBuffer;

	 VkDeviceMemory indexBufferMemory;

	 std::vector<VkBuffer> uniformBuffers;
	 std::vector<VkDeviceMemory> uniformBuffersMemory;
	 std::vector<void*> uniformBuffersMapped;

	 // VkDescriptorPool是一个对象，表示描述符池
	 VkDescriptorPool descriptorPool;

	 // VkDescriptorSet用于绑定资源到图形或计算管线的对象
	 std::vector<VkDescriptorSet> descriptorSets;

	 // 初始化窗口
	 void initWindow() {
		 // glfwInit是初始化GLFW内部状态，必须在调用任何其他GLFW函数之前执行
		 glfwInit();
		 // 固定写法，这是创建的是Vulkan的上下文
		 glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
		 // 设置禁止窗口大小改变
		 // glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
		 // 创建窗口
		 // glfwCreateWindow函数前三个参数设置窗口宽度、高度和标题，
		 // 第四个参数指定在那个显示屏打开窗口，最后一个设置为nullptr
		 window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr);
		
		 // glfwSetWindowUserPointer是GLFW的函数，用于将用户定义的指针与GLFWwindow关联起来
		 glfwSetWindowUserPointer(window, this);
		 // glfwSetFramebufferSizeCallback是GLFW库提供的函数，用于在帧缓冲区大小发生变化时注册回调函数
		 glfwSetFramebufferSizeCallback(window, framebufferResizeCallback);
	 }
	
	 static void framebufferResizeCallback(GLFWwindow* window, int width, int height) {
		 // glfwGetWindowUserPointer是GLFW的函数，用于从GLFWwindow对象中检索之前通过glfwSetWindowUserPointer存储的用户自定义指针
		 auto app = reinterpret_cast<HelloTriangleApplication*>(glfwGetWindowUserPointer(window));
		 app->framebufferResized = true;
	 }

	 // initVulkan函数来初始化Vulkan对象
	void initVulkan() {
		createInstance();
		setupDebugMessenger();
		createSurface();
		pickPhysicalDevice();
		createLogicalDevice();
		createSwapChain();
		createImageViews();
		createRenderPass();
		createDescriptorSetLayout();
		createGraphicsPipeline();
		createCommandPool();
		createColorResources();
		createDepthResources();
		createFramebuffers();
		createTextureImage();
		createTextureImageView();
		createTextureSampler();
		loadModel();
		createVertexBuffer();
		createIndexBuffer();
		createUniformBuffers();
		createDescriptorPool();
		createDescriptorSets();
		createCommandBuffers();
		createSyncObjects();
	}

	void loadModel() {
		// tinyobj::attrib_t是TinyOBJLoader的结构，用于存储.obj文件中的顶点属性数据，包括顶点位置、法线、纹理坐标等
		tinyobj::attrib_t attrib;
		// tinyobj::shape_t是TinyOBJLoader的结构，用于表示一个几何形状，包含了网格数据及该网络相关的属性
		std::vector<tinyobj::shape_t> shapes;
		// tinyobj::material_t是TinyOBJLoader的结构,用于存储.obj文件中的材质信息
		std::vector<tinyobj::material_t> materials;
		std::string err;

		// tinyobj::LoadObj是TinyOBJLoader库函数，用于加载.obj函数，并将其解析为可操作的数据结构
		if (!tinyobj::LoadObj(&attrib, &shapes, &materials, nullptr, &err, MODEL_PATH.c_str())) {
			throw std::runtime_error(err);
		}

		// std::unordered_map是一个高效的容器，适用于需要快速查找和存储键值对的场景，
		// 它不保证元素的顺序，但提供了比std::map更高效的查找操作
		std::unordered_map<Vertex, uint32_t> uniqueVertices{};

		for (const auto& shape : shapes) {
			// shape.mesh.indices存储了顶点的索引信息
			for (const auto& index : shape.mesh.indices) {
				Vertex vertex{};
				
				vertex.pos = {
					attrib.vertices[3 * index.vertex_index + 0], 
					attrib.vertices[3 * index.vertex_index + 1],
					attrib.vertices[3 * index.vertex_index + 2]
				};

				vertex.texCoord = {
					attrib.texcoords[2 * index.texcoord_index + 0],
					1.0f - attrib.texcoords[2 * index.texcoord_index + 1]
				};

				vertex.color = {1.0f, 1.0f, 1.0f};

				if (uniqueVertices.count(vertex) == 0) {
					uniqueVertices[vertex] = static_cast<uint32_t>(vertices.size());
					vertices.push_back(vertex);
				}

				indices.push_back(uniqueVertices[vertex]);
			}
		}
	}

	void createColorResources() {
		VkFormat colorFormat = swapChainImageFormat;
		
		createImage(
			swapChainExtent.width,
			swapChainExtent.height,
			1,
			msaaSamples,
			colorFormat,
			VK_IMAGE_TILING_OPTIMAL,
			VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
			VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
			colorImage,
			colorImageMemory
			);

		colorImageView = createImageView(colorImage, colorFormat, VK_IMAGE_ASPECT_COLOR_BIT, 1);
	}

	void createDepthResources() {
		VkFormat depthFormat = findDepthFormat();
		createImage(
			swapChainExtent.width, 
			swapChainExtent.height, 
			1, 
			msaaSamples, 
			depthFormat, 
			VK_IMAGE_TILING_OPTIMAL, 
			VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, 
			VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, 
			depthImage, 
			depthImageMemory
		);

		depthImageView = createImageView(depthImage, depthFormat, VK_IMAGE_ASPECT_DEPTH_BIT, 1);

		// transitionImageLayout(depthImage, depthFormat, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
	}

	// VkFormatFeatureFlages用于描述图像或缓冲区格式特性的一组标志位
	VkFormat findSupportedFormat(const std::vector<VkFormat>& candidates, VkImageTiling tiling, VkFormatFeatureFlags features) {
		for (VkFormat format : candidates) {
			// VkFormatProperties用于存储于特定格式相关的属性结构体
			// linearTilingFeatures 描述格式在线性铺展模式下支持的特性
			// optimalTilingFeatures 描述格式在最佳铺展模式下支持的特性
			// bufferFeatures 描述格式在缓冲区模式下支持的特性
			VkFormatProperties props;
			// vkGetPhysicalDeviceFormatProperties用于查询物理设备对特定格式的支持情况
			vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &props);

			if (tiling == VK_IMAGE_TILING_LINEAR && (props.linearTilingFeatures & features) == features) {
				return format;
			}
			else if(tiling == VK_IMAGE_TILING_OPTIMAL && (props.optimalTilingFeatures & features) == features){
				return format;
			}
		}
		throw std::runtime_error("failed to find supported format!");
	}

	VkFormat findDepthFormat() {
		return findSupportedFormat(
			{ VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT },
			VK_IMAGE_TILING_OPTIMAL,
			VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT
		);
	}

	bool hasStencilComponent(VkFormat format) {
		return format == VK_FORMAT_D32_SFLOAT_S8_UINT || format == VK_FORMAT_D24_UNORM_S8_UINT;
	}

	void createTextureSampler() {
		// VkPhysicalDeviceProperties用于描述物理设备的各种属性
		VkPhysicalDeviceProperties properties{};
		vkGetPhysicalDeviceProperties(physicalDevice, &properties);

		// VkSamplerCreateInfo用于描述采样器对象的结构体
		// sType 结构体标识
		// magFilter 放大过滤方式，指定当纹理被放大时如何进行插值
		// minFilter 缩小过滤方式，指定当纹理被缩小时如何进行插值
		// addressModeU addressModeV addressModeW 纹理坐标的包裹方式，控制纹理坐标超出[0,1]范围时的行为
		// anisotropyEnable 是否启用各项异性过滤
		// maxAnisotropy 启用各项异性过滤时，最大各项异性级别，通常设置为1.0（无各向异性过滤）到某个最大值
		// borderColor 边界颜色
		// unnormalizedCoordinates 是否启用非标准化坐标，若启用，[0, texWidth)和[0, texHeight)，若不启用，[0, 1)
		// compareEnable 是否启用比较操作
		// compareOp 指定比较操作的类型
		// mipmapMode 指定mipmap的滤波模式
		// mipLodBias LOD（mipmap层级）的偏移量
		// minLod 最小LOD层级
		// maxLod 最大LOD层级
		VkSamplerCreateInfo samplerInfo{};
		samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
		samplerInfo.magFilter = VK_FILTER_LINEAR;
		samplerInfo.minFilter = VK_FILTER_LINEAR;
		samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
		samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
		samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
		samplerInfo.anisotropyEnable = VK_TRUE;
		samplerInfo.maxAnisotropy = properties.limits.maxSamplerAnisotropy;
		samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
		samplerInfo.unnormalizedCoordinates = VK_FALSE;
		samplerInfo.compareEnable = VK_FALSE;
		samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;

		samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
		// samplerInfo.mipLodBias = 0.0f;
		// samplerInfo.minLod = 0.0f;
		samplerInfo.maxLod = VK_LOD_CLAMP_NONE;

		// vkCreateSampler用于创建纹理采样器对象的函数
		if (vkCreateSampler(device, &samplerInfo, nullptr, &textureSampler) != VK_SUCCESS) {
			throw std::runtime_error("failed to create texture sampler!");
		}
	}

	void createTextureImageView() {
		textureImageView = createImageView(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_ASPECT_COLOR_BIT, mipLevels);
	}

	VkImageView createImageView(VkImage image, VkFormat format, VkImageAspectFlags aspectFlags, uint32_t mipLevels) {
		VkImageViewCreateInfo viewInfo{};
		viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
		viewInfo.image = image;
		viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
		viewInfo.format = format;
		viewInfo.subresourceRange.aspectMask = aspectFlags;
		viewInfo.subresourceRange.baseMipLevel = 0;
		viewInfo.subresourceRange.levelCount = mipLevels;
		viewInfo.subresourceRange.baseArrayLayer = 0;
		viewInfo.subresourceRange.layerCount = 1;

		VkImageView imageView;

		if (vkCreateImageView(device, &viewInfo, nullptr, &imageView) != VK_SUCCESS) {
			throw std::runtime_error("failed to create image view!");
		}

		return imageView;
	}

	// VkImageLayout用于表示图像当前布局的枚举类型，指示了图像在特定时间内的使用方式
	void transitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout, uint32_t mipLevels) {
		VkCommandBuffer commandBuffer = beginSingleTimeCommands();
		
		// VkImageMemoryBarrier用于图像内存屏障的结构体，用于管理图像资源在渲染或计算操作之间的同步
		// sType 结构体标识
		// oldLayout 图像转换前的布局，表示图像在源操作时的布局状态
		// newLayout 图像转换后的布局，表示图像在目标操作时的布局状态
		// srcQueueFamilyIndex 源队列族索引，表示源操作所在的队列族，如果不需要队列族同步，必须设置为VK_QUEUE_FAMILY_IGNORED
		// dstQueueFamilyIndex 目标队列族索引，表示目标操作所在的队列族，如果不需要队列族同步，必须设置为VK_QUEUE_FAMILY_IGNORED
		// image 图像对象句柄，指定操作的目标图像
		// subresourceRange指定图像的子资源范围
		// subresourceRange.aspectMask 指定图像的子资源方面，决定了图像的那些部分需要进行操作
		// subresourceRange.baseMipLevel 指定子资源范围的其实mip级别
		// subresourceRange.levelCount 指定需要操作的mip级别数量
		// subresourceRange.baseArrayLayer 指定子资源范围的起始数组层
		// subresourceRange.layerCount 指定需要操作的数组层范围
		// srcAccessMask 指定了图像在转换前的访问类型
		// dstAccessMask 指定了图像在转换后的访问类型
		VkImageMemoryBarrier barrier{};
		barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; 
		barrier.oldLayout = oldLayout;
		barrier.newLayout = newLayout;

		barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
		barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;

		barrier.image = image;
		// if (newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) {
		// 	barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
		// 	if (hasStencilComponent(format)) {
		// 		barrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
		// 	}
		// }
		// else {
		// 	barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
		// }

		barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
		barrier.subresourceRange.baseMipLevel = 0;
		barrier.subresourceRange.levelCount = 1;
		barrier.subresourceRange.baseArrayLayer = 0;
		barrier.subresourceRange.layerCount = mipLevels;

		VkPipelineStageFlags sourceStage;
		VkPipelineStageFlags destinationStage;

		if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
			barrier.srcAccessMask = 0;
			barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
			sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
			destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
		}
		else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
			barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
			barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;

			sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
			destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
		}
		// else if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) {
		// 	barrier.srcAccessMask = 0;
		// 	barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;

		// 	sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
		// 	destinationStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;	
		// }
		else {
			throw std::invalid_argument("unsupported layout transition!");
		}

		// barrier.srcAccessMask = 0;
		// barrier.dstAccessMask = 0;
		
		// vkCmdPipelineBarrier用于执行命令缓冲区中的管线屏障操作的函数
		vkCmdPipelineBarrier(commandBuffer, 
			sourceStage, destinationStage, 
			0, 
			0, nullptr, 
			0, nullptr, 
			1, &barrier);

		endSingleTimeCommands(commandBuffer);
	}

	void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height) {
		VkCommandBuffer commandBuffer = beginSingleTimeCommands();

		// VkBufferImageCopy用于执行缓冲区与图像之间数据拷贝的结构体
		// bufferOffset 指定缓冲区中数据的偏移量
		// bufferRowLength 指定缓冲区中每一行的字节数
		// bufferImageHeight 指定缓冲区图像高度；如果为0，则表示图像只有一行（即图像数据直接紧接）。如果为非零值，表示每个图像的每一行之间有额外的字节填充
		// imageSubresource指定目标图像的子资源层级
		// imageOffset 指定图像数据拷贝的起始偏移
		// imageExtent 指定拷贝数据的区域大小
		VkBufferImageCopy region{};
		region.bufferOffset = 0;
		region.bufferRowLength = 0;
		region.bufferImageHeight = 0;

		region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
		region.imageSubresource.mipLevel = 0;
		region.imageSubresource.baseArrayLayer = 0;
		region.imageSubresource.layerCount = 1;

		region.imageOffset = {0, 0, 0};
		region.imageExtent = {
			width, 
			height, 
			1
		};

		// vkCmdCopyBufferToImage用于将缓冲区（VkBuffer）中的数据拷贝到图像（VkImage）的命令
		vkCmdCopyBufferToImage(commandBuffer, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);

		endSingleTimeCommands(commandBuffer);
	}

	VkCommandBuffer beginSingleTimeCommands() {
		VkCommandBufferAllocateInfo allocInfo{};
		allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
		allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
		allocInfo.commandPool = commandPool;
		allocInfo.commandBufferCount = 1;
		
		VkCommandBuffer commandBuffer;

		vkAllocateCommandBuffers(device, &allocInfo, &commandBuffer);

		VkCommandBufferBeginInfo beginInfo{};
		beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
		beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
		vkBeginCommandBuffer(commandBuffer, &beginInfo);

		return commandBuffer;
	}

	void endSingleTimeCommands(VkCommandBuffer commandBuffer) {
		vkEndCommandBuffer(commandBuffer);

		VkSubmitInfo submitInfo{};

		submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
		submitInfo.commandBufferCount = 1;
		submitInfo.pCommandBuffers = &commandBuffer;

		vkQueueSubmit(graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
		vkQueueWaitIdle(graphicsQueue);

		vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);
	}

	void createTextureImage() {
		int texWidth, texHeight, texChannels;
		// stbi_load用于从文件或内存加载图像数据到像素数组中
		stbi_uc* pixels = stbi_load(TEXTURE_PATH.c_str(), &texWidth, &texHeight, &texChannels, STBI_rgb_alpha);
		
		// std::floor向下取整
		// std::log2是C++标准库的数学函数，用于计算以2为底的对数
		mipLevels = static_cast<uint32_t>(std::floor(std::log2(std::max(texWidth, texHeight)))) + 1;

		VkDeviceSize imageSize = texWidth * texHeight * 4;
		if (!pixels) {
			throw std::runtime_error("failed to load texture image!");
		}

		VkBuffer stagingBuffer;
		VkDeviceMemory stagingBufferMemory;

		createBuffer(imageSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
			VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory);

		void* data;

		vkMapMemory(device, stagingBufferMemory, 0, imageSize, 0, &data);
		memcpy(data, pixels, static_cast<size_t>(imageSize));
		vkUnmapMemory(device, stagingBufferMemory);

		stbi_image_free(pixels);

		createImage(texWidth, texHeight, mipLevels, VK_SAMPLE_COUNT_1_BIT, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | 
			VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, textureImage, textureImageMemory);

		transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, mipLevels);

		copyBufferToImage(stagingBuffer, textureImage, static_cast<uint32_t>(texWidth), static_cast<uint32_t>(texHeight));

		// transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, mipLevels);

		vkDestroyBuffer(device, stagingBuffer, nullptr);
		vkFreeMemory(device, stagingBufferMemory, nullptr);

		generateMipmaps(textureImage, VK_FORMAT_R8G8B8A8_SRGB, texWidth, texHeight, mipLevels);
	}

	void generateMipmaps(VkImage image, VkFormat imageFormat, int32_t texWidth, int32_t texHeight, uint32_t mipLevels) {
		VkFormatProperties formatProperties;
		vkGetPhysicalDeviceFormatProperties(physicalDevice, imageFormat, &formatProperties);

		if (!(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT)) {
			throw std::runtime_error("texture image format does not support linear blitting!");
		}
		
		VkCommandBuffer commandBuffer = beginSingleTimeCommands();
		
		// VkImageMemoryBarrier用于在命令缓存区中进行图像资源的内存与布局同步
		VkImageMemoryBarrier barrier{};
		barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
		barrier.image = image;
		barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
		barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
		barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
		barrier.subresourceRange.baseArrayLayer = 0;
		barrier.subresourceRange.layerCount = 1;
		barrier.subresourceRange.levelCount = 1;

		int32_t mipWidth = texWidth;
		int32_t mipHeight = texHeight;

		for (uint32_t i = 1; i < mipLevels; i++) {
			barrier.subresourceRange.baseMipLevel = i - 1;
			barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
			barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
			barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
			barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;

			vkCmdPipelineBarrier(
				commandBuffer, 
				VK_PIPELINE_STAGE_TRANSFER_BIT, 
				VK_PIPELINE_STAGE_TRANSFER_BIT, 
				0, 
				0, 
				nullptr, 
				0, 
				nullptr, 
				1, &barrier
			);
			
			// VKImageBlit表示图像区域的拷贝和转换操作
			// srcSubresource 源图像的子资源类型
			// srcOffsets[2] 源图像的起始和结束坐标（偏移量），是VkOffset3D类型的数组
			// dstSubresource目标图像的子资源类型
			// dstOffsets[2] 目标图像的起始和结束坐标（偏移量），是VkOffset3D类型的数组
			VkImageBlit blit{};
			blit.srcOffsets[0] = { 0, 0, 0 };
			blit.srcOffsets[1] = { mipWidth, mipHeight, 1 };
			blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
			blit.srcSubresource.mipLevel = i - 1;
			blit.srcSubresource.baseArrayLayer = 0;
			blit.srcSubresource.layerCount = 1;

			blit.dstOffsets[0] = { 0, 0, 0 };
			blit.dstOffsets[1] = { mipWidth > 1 ? mipWidth / 2 : 1, mipHeight > 1 ? mipHeight / 2 : 1, 1 };
			blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
			blit.dstSubresource.mipLevel = 1;
			blit.dstSubresource.baseArrayLayer = 0;
			blit.dstSubresource.layerCount = 1;

			// vkCmdBlitImage将一个图像区域拷贝到另一个图像，并且支持图像的缩放、格式转换等操作
			vkCmdBlitImage(
				commandBuffer, 
				image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, 
				image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 
				1, &blit, 
				VK_FILTER_LINEAR
			);

			barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
			barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
			barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
			barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;

			vkCmdPipelineBarrier(
				commandBuffer, 
				VK_PIPELINE_STAGE_TRANSFER_BIT, 
				VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 
				0, nullptr, 
				0, nullptr, 
				1, &barrier
			);

			if (mipWidth > 1) mipWidth /= 2;
      if (mipHeight > 1) mipHeight /= 2;
		}

		barrier.subresourceRange.baseMipLevel = mipLevels - 1;
		barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
		barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
		barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
		barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;

		vkCmdPipelineBarrier(
			commandBuffer, 
			VK_PIPELINE_STAGE_TRANSFER_BIT, 
			VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 
			0, 
			0, 
			nullptr, 
			0, 
			nullptr, 
			1, 
			&barrier
		);

		endSingleTimeCommands(commandBuffer);
	}

	VkSampleCountFlagBits getMaxUsableSampleCount() {
		VkPhysicalDeviceProperties physicalDeviceProperties;
		vkGetPhysicalDeviceProperties(physicalDevice, &physicalDeviceProperties);

		VkSampleCountFlags counts = physicalDeviceProperties.limits.framebufferColorSampleCounts & physicalDeviceProperties.limits.framebufferDepthSampleCounts;
		if (counts & VK_SAMPLE_COUNT_64_BIT) { return VK_SAMPLE_COUNT_64_BIT; }
		if (counts & VK_SAMPLE_COUNT_32_BIT) { return VK_SAMPLE_COUNT_32_BIT; }
		if (counts & VK_SAMPLE_COUNT_16_BIT) { return VK_SAMPLE_COUNT_16_BIT; }
		if (counts & VK_SAMPLE_COUNT_8_BIT) { return VK_SAMPLE_COUNT_8_BIT; }
		if (counts & VK_SAMPLE_COUNT_4_BIT) { return VK_SAMPLE_COUNT_4_BIT; }
		if (counts & VK_SAMPLE_COUNT_2_BIT) { return VK_SAMPLE_COUNT_2_BIT; }

		return VK_SAMPLE_COUNT_1_BIT;
	}

	void createDescriptorSets() {
		// VkDescriptorSetLayout用于描述描述符集合（VkDescriptorSet）结构的对象
		std::vector<VkDescriptorSetLayout> layouts(MAX_FRAMES_IN_FLIGHT, descriptorSetLayout);
		// VkDescriptorSetAllocateInfo用于描述分配描述符集合（VkDescriptorSet）的信息
		// sType 结构体标识
		// descriptorPool VkDescriptorPool对象的句柄，分配的描述符集合会从该池中分配资源
		// descriptorSetCount 需要分配的描述符集合数量
		// pSetLayouts 指向VkDescritporSetLayout数组的指针，定义了每个描述符集合的布局
		VkDescriptorSetAllocateInfo allocInfo{};
		allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
		allocInfo.descriptorPool = descriptorPool;
		allocInfo.descriptorSetCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
		allocInfo.pSetLayouts = layouts.data();

		descriptorSets.resize(MAX_FRAMES_IN_FLIGHT);
		// vkAllocateDescriptorSets用于从描述符池（VkDescriptorPool）中分配描述符集合（VkDescriptorSet）的函数
		if (vkAllocateDescriptorSets(device, &allocInfo, descriptorSets.data()) != VK_SUCCESS) {
			throw std::runtime_error("failed to allocate descriptor sets!");
		}

		for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
			// VkDescriptorBufferInfo用于描述一个缓冲区（VkBuffer）在描述符集合中的绑定信息
			// buffer 绑定的缓冲区句柄
			// offset 缓冲区的偏移量
			// range 缓冲区的范围	
			VkDescriptorBufferInfo bufferInfo{};
			bufferInfo.buffer = uniformBuffers[i];
			bufferInfo.offset = 0;
			bufferInfo.range = sizeof(UniformBufferObject);

			// VkDescriptorImageInfo用于描述与图像资源相关的描述符信息
			// imageLayout 图像的布局
			// imageView 图像视图对象，用于描述如何访问图像资源
			// sampler 采样器对象，用于定义图像采样的方式
			VkDescriptorImageInfo imageInfo{};
			imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
			imageInfo.imageView = textureImageView;
			imageInfo.sampler = textureSampler;

			// VkWriteDescriptorSet用于更新描述符集的结构体
			// sType 结构体标识
			// dstSet 目标描述符集的句柄，即你想要更新的描述符集
			// dstBinding 描述符绑定的索引，表示资源应该绑定到的管线阶段的那个位置
			// dstArrayElement 该描述符数组中的起始元素
			// descriptorType 描述符类型，指定资源的类型
			// descriptorCount 描述符的数量
			// pImageInfo 指向描述符图像信息数组的指针
			// pBufferInfo 指向描述符缓冲区信息数组的指针
			// pTexelBufferView 指向描述符纹理缓冲区视图的指针
			std::array<VkWriteDescriptorSet, 2> descriptorWrites{};
			descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
			descriptorWrites[0].dstSet = descriptorSets[i];
			descriptorWrites[0].dstBinding = 0;
			descriptorWrites[0].dstArrayElement = 0;
			descriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
			descriptorWrites[0].descriptorCount = 1;
			descriptorWrites[0].pBufferInfo = &bufferInfo;
			descriptorWrites[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
			descriptorWrites[1].dstSet = descriptorSets[i];
			descriptorWrites[1].dstBinding = 1;
			descriptorWrites[1].dstArrayElement = 0;
			descriptorWrites[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
			descriptorWrites[1].descriptorCount = 1;
			descriptorWrites[1].pImageInfo = &imageInfo;
			// descriptorWrite.pImageInfo = nullptr;
			// descriptorWrite.pTexelBufferView = nullptr;
			
			// vkUpdateDescriptorSets用来更新描述符集的函数
			vkUpdateDescriptorSets(device, static_cast<uint32_t>(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr);
		}
	}

	void createDescriptorPool() {
		// VkDescriptorPoolSize用于描述资源池中每种资源类型数量的结构体
		// type 描述池中描述符的类型
		// descriptorCount 该池中指定类型描述符的数量
		std::array<VkDescriptorPoolSize, 2> poolSizes{};
		poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
		poolSizes[0].descriptorCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
		poolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
		poolSizes[1].descriptorCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
		
		// VkDescriptorPoolCreateInfo用于描述如何创建描述符池的结构体
		// sType 结构体标识
		// poolSizeCount 数组的元素数量，即池中不同类型描述符的数量
		// pPoolSizes 指向VkDescriptorPoolSize数组的指针，定义了描述符池中每种类型描述符的数量
		// maxSets 描述符池中可以分配的最大描述符集合数量
		VkDescriptorPoolCreateInfo poolInfo{};
		poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
		poolInfo.poolSizeCount = static_cast<uint32_t>(poolSizes.size());
		poolInfo.pPoolSizes = poolSizes.data();
		poolInfo.maxSets = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);

		// vkCreateDescriptorPool用于创建描述符池（VkDescriptorPool）的函数
		if (vkCreateDescriptorPool(device, &poolInfo, nullptr, &descriptorPool) != VK_SUCCESS) {
			throw std::runtime_error("failed to create descriptor pool!");
		}
	}

	void createUniformBuffers() {
		VkDeviceSize bufferSize = sizeof(UniformBufferObject);
		uniformBuffers.resize(MAX_FRAMES_IN_FLIGHT);
		uniformBuffersMemory.resize(MAX_FRAMES_IN_FLIGHT);
		uniformBuffersMapped.resize(MAX_FRAMES_IN_FLIGHT);

		for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
			createBuffer(bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, 
				VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | 
				VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, uniformBuffers[i], uniformBuffersMemory[i]);

			vkMapMemory(device, uniformBuffersMemory[i], 0, bufferSize, 0, &uniformBuffersMapped[i]);
		}
	}

	 void createDescriptorSetLayout() {
		 // VkDescriptorSetLayoutBinding用于描述做色漆资源绑定布局的关联结构体
		 // binding 描述符绑定号
		 // descriptorType 指定绑定的资源类型（即描述符类型）
		 // descriptorCount 绑定的资源数量
		 VkDescriptorSetLayoutBinding uboLayoutBinding{};
		 uboLayoutBinding.binding = 0;
		 uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
		 uboLayoutBinding.descriptorCount = 1;
		 uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
		 // uboLayoutBinding.pImmutableSamplers = nullptr;
			
		 // VkDescriptorSetLayoutBinding用于描述一个描述符集布局中的单个绑定
		 // binding 描述符集布局中绑定点的索引
		 // descriptorCount 该绑定中描述符的数量
		 // descriptorType 描述符的类型，指定该绑定所对应的资源类型
		 // pImmutableSamplers 如果绑定类型是图像或采样器，且采样器是不可变的，可以通过该字段指定一个不可变的采样器数组
		 // stageFlags 该绑定所属的着色器阶段
		 VkDescriptorSetLayoutBinding samplerLayoutBinding{};
		 samplerLayoutBinding.binding = 1;
		 samplerLayoutBinding.descriptorCount = 1;
		 samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
		 samplerLayoutBinding.pImmutableSamplers = nullptr;
		 samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;

		 std::array<VkDescriptorSetLayoutBinding, 2> bindings = {uboLayoutBinding, samplerLayoutBinding};
		
		 // VkDescriptorSetLayoutCreateInfo用于描述如何创建描述符集布局的结构体
		 // sType 结构体标识
		 // bindingCount 绑定的数量
		 // pBindings 指向绑定信息的数组
		 VkDescriptorSetLayoutCreateInfo layoutInfo{};
		 layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
		 layoutInfo.bindingCount = static_cast<uint32_t>(bindings.size());
		 layoutInfo.pBindings = bindings.data();

		 // vkCreateDescriptorSetLayout用于创建描述符集布局的函数
		 if (vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, &descriptorSetLayout) != VK_SUCCESS) {
			 throw std::runtime_error("failed to create descriptor set layout!");
		 }
	 }

	void createIndexBuffer() {
		VkDeviceSize bufferSize = sizeof(indices[0]) * indices.size();

		VkBuffer stagingBuffer;
		VkDeviceMemory stagingBufferMemory;
		createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, 
			VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | 
			VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, 
			stagingBuffer, stagingBufferMemory);

		void* data;
		vkMapMemory(device, stagingBufferMemory, 0, bufferSize, 0, &data);
		memcpy(data, indices.data(), (size_t)bufferSize);
		vkUnmapMemory(device, stagingBufferMemory);

		createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | 
			VK_BUFFER_USAGE_INDEX_BUFFER_BIT, 
			VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, indexBuffer, indexBufferMemory);

		copyBuffer(stagingBuffer, indexBuffer, bufferSize);

		vkDestroyBuffer(device, stagingBuffer, nullptr);
		vkFreeMemory(device, stagingBufferMemory, nullptr);
	}

	void createBuffer(
		// VkDeviceSize表示设备内存中某一区域的大小
		VkDeviceSize size, 
		// VkBufferUsageFlags用于描述缓冲区使用方式的标志位
		VkBufferUsageFlags usage, 
		// VkMemoryPropertyFlags用于描述内存属性的标志位
		VkMemoryPropertyFlags properties, 
		VkBuffer& buffer, 
		VkDeviceMemory& bufferMemory) {
		// VkBufferCreateInfo是用来创建缓冲区（VkBuffer）的结构体
		// sType 结构体标识
		// size 缓冲区的大小，单位为字节
		// usage 指定缓冲区的用途，VK_BUFFER_USAGE_TRANSFER_SRC_BIT（表示缓冲区作为数据传输的源）、
		// VK_BUFFER_USAGE_TRANSFER_DST_BIT（表示缓冲区作为数据传输的目标）、
		// VK_BUFFER_USAGE_VERTEX_BUFFER_BIT（表示缓冲区作为顶点缓冲区）、
		// VK_BUFFER_USAGE_INDEX_BUFFER_BIT（表示缓冲区作为索引缓冲区）、
		// VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT（表示缓冲区作为统一缓冲区）
		// sharingMode 用来指定缓冲区的共享模式，VK_SHARING_MODE_EXCLUSIVE（缓冲区只能由一个队列使用）、
		// VK_SHARING_MODE_CONCURRENT（缓冲区可以由多个队列共享）、
		VkBufferCreateInfo bufferInfo{};
		bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
		bufferInfo.size = size;
		bufferInfo.usage = usage;
		bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;

		// vkCreateBuffer用来创建缓冲区（VkBuffer）对象的函数
		if (vkCreateBuffer(device, &bufferInfo, nullptr, &buffer) != VK_SUCCESS) {
			throw std::runtime_error("failed to create vertex buffer!");
		}
		// VkMemoryRequirements用于描述对象（如缓冲区、图像等）所需的内存资源
		// size 需要的内存大小（以字节为单位）
		// alignment 分配的内存必须是某个对齐值的倍数，内存起始地址的对齐要求
		// memoryTypeBits 适合缓冲区的内存类型的位字段
		VkMemoryRequirements memRequirements;
		// vkGetBufferMemoryRequirements用于查询与缓冲区（VkBuffer）相关的内存要求
		vkGetBufferMemoryRequirements(device, buffer, &memRequirements);

		// VkMemoryAllocateInfo用于描述内存分配请求的结构体
		// sType 结构体标识
		// allocationSize 需要分配的内存大小，单位是字节
		// memoryTypeIndex 指定内存类型的索引
		VkMemoryAllocateInfo allocInfo{};
		allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
		allocInfo.allocationSize = memRequirements.size;
		allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties);

		// vkAllocateMemory用于分配设备内存的函数
		if (vkAllocateMemory(device, &allocInfo, nullptr, &bufferMemory) != VK_SUCCESS) {
			throw std::runtime_error("failed to allocate vertex buffer memory!");
		}

		// vkBindBufferMemory用来将设备内存绑定到缓冲区的函数
		vkBindBufferMemory(device, buffer, bufferMemory, 0);

	}

	void createVertexBuffer() {
		VkDeviceSize bufferSize = sizeof(vertices[0]) * vertices.size();
		VkBuffer stagingBuffer;
		VkDeviceMemory stagingBufferMemory;
		createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, 
			VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | 
			VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, 
			stagingBuffer, stagingBufferMemory
			);
		void* data;
		// vkMapMemory用于将设备内存映射到主机内存地址的函数
		vkMapMemory(device, stagingBufferMemory, 0, bufferSize, 0, &data);
		memcpy(data, vertices.data(), (size_t)bufferSize);
		// vkUnmapMemory用于解除内存映射的函数
		vkUnmapMemory(device, stagingBufferMemory);

		createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, 
			VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, vertexBuffer, vertexBufferMemory);

		copyBuffer(stagingBuffer, vertexBuffer, bufferSize);

		vkDestroyBuffer(device, stagingBuffer, nullptr);

		vkFreeMemory(device, stagingBufferMemory, nullptr);
	}
	
	// VkMemoryPropertyFlags用于描述内存的属性标志类型
	// VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT（表示内存位于设备本地，通常是GPU的内存，通常用于存储GPU渲染数据）、
	// VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT（表示内存可从主机（CPU）访问，这种内存用于与主机进行数据交换）、
	// VK_MEMORY_PROPERTY_HOST_COHERENT_BIT（表示内存的修改对主机可见，表示内存对CPU和GPU都可见）、
	// VK_MEMORY_PROPERTY_HOST_CACHED_BIT（表示内存是缓存的，修改后可以从CPU缓存中读取）、
	// VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT（表示内存是惰性分配的，是到实际使用时才分配物理内存）、
	// VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT（仅适用于AMD的硬件，表示内存的修改对GPU是可见的）、
	// VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD（仅适用于AMD的硬件，表示内存是不可缓存的，通常用于GPU执行直接访问，避免CPU或GPU缓存引发的性能问题）
	uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) {
		// VkPhysicalDeviceMemoryProperties用于查询物理设备的内存属性，包含了该设备可用的内存类型和内存堆的相关信息
		// memoryTypeCount 设备支持的内存类型数量
		// memoryType 设备支持的内存类型数组
		// memoryHeapCount 设备支持的内存堆的数量
		// memoryHeap 设备支持的内存堆的数组
		VkPhysicalDeviceMemoryProperties memProperties;
		// vkGetPhysicalDeviceMemoryProperties用于查询物理设备的内存属性
		vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProperties);

		for (uint32_t i = 0; i < memProperties.memoryTypeCount; ++i) {
			if (typeFilter & (1 << i) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) {
				return i;
			}
		}
		throw std::runtime_error("failed to find suitable memory type!");
	}

	void copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size) {
		// VkCommandBufferAllocateInfo allocInfo{};
		// allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
		// allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
		// allocInfo.commandPool = commandPool;
		// allocInfo.commandBufferCount = 1;

		// VkCommandBuffer commandBuffer;
		// vkAllocateCommandBuffers(device, &allocInfo, &commandBuffer);

		// VkCommandBufferBeginInfo beginInfo{};
		// beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
		// beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;

		// vkBeginCommandBuffer(commandBuffer, &beginInfo);

		// // VkBufferCopy用于描述缓冲区复制操作的数据结构
		// // srcOffset 指定源缓冲区的数据复制起始位置
		// // dstOffset 指定目标缓冲区的数据复制起始位置
		// // size 表示复制的字节数，即从源缓冲区复制到目标缓冲区的数据大小
		// VkBufferCopy copyRegion{};
		// // copyRegion.srcOffset = 0;
		// // copyRegion.dstOffset = 0;
		// copyRegion.size = size;
		// 
		// // vkCmdCopyBuffer用于在命令缓冲区添加一个复制操作，执行从一个缓冲区到另一个缓冲区的数据复制
		// vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, &copyRegion);

		// vkEndCommandBuffer(commandBuffer);

		// VkSubmitInfo submitInfo{};
		// submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
		// submitInfo.commandBufferCount = 1;
		// submitInfo.pCommandBuffers = &commandBuffer;
		// vkQueueSubmit(graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
		// // vkQueueWaitIdle用于使得队列上的所有命令完成执行，直到队列中所有的命令都执行完毕
		// vkQueueWaitIdle(graphicsQueue);
		// // vkFreeCommandBuffers用于释放命令缓冲区（VkCommandBuffer）的函数
		// vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);

		VkCommandBuffer commandBuffer = beginSingleTimeCommands();
		VkBufferCopy copyRegion{};
		copyRegion.size = size;
		vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, &copyRegion);

		endSingleTimeCommands(commandBuffer);
	}

	void createSyncObjects() {
		imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
		renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
		inFlightFences.resize(MAX_FRAMES_IN_FLIGHT);
		// VkSemaphoreCreateInfo是用于创建VkSemaphore（信号量）对象的结构体
		// sType 结构体标识
		VkSemaphoreCreateInfo semaphoreInfo{};
		semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
		// VkFenceCreateInfo是用于创建VkFence（栅栏）对象的结构体
		// sType 结构体标识
		// flags VkFenceCreateInfo类型的标志，指定fence对象的创建类型
		VkFenceCreateInfo fenceInfo{};
		fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
		fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
		for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) {
			// vkCreateSemaphore用于创建信号量（VkSemaphore）的函数
			if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphores[i]) != VK_SUCCESS ||
				vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphores[i]) != VK_SUCCESS ||
				// vkCreateFence用于创建栅栏（VkFence）对象的函数
				vkCreateFence(device, &fenceInfo, nullptr, &inFlightFences[i]) != VK_SUCCESS) {
				throw std::runtime_error("failed to create semaphores!");
			}
		}
	}

	void cleanupSwapChain() {
		vkDestroyImageView(device, depthImageView, nullptr);
		vkDestroyImage(device, depthImage, nullptr);
		vkFreeMemory(device, depthImageMemory, nullptr);

		vkDestroyImageView(device, colorImageView, nullptr);
		vkDestroyImage(device, colorImage, nullptr);
		vkFreeMemory(device, colorImageMemory, nullptr);

		for (auto framebuffer : swapChainFramebuffers) {
			// vkDestroyFramebuffer用于销毁帧缓冲（VkFramebuffer）对象的函数
			vkDestroyFramebuffer(device, framebuffer, nullptr);
		}
		for (auto imageView : swapChainImageViews) {
			// vkDestroyImageView 用于销毁图像视图的函数
			vkDestroyImageView(device, imageView, nullptr);
		}
		// vkDestroySwapchainKHR是用来销毁交换链的函数
		vkDestroySwapchainKHR(device, swapChain, nullptr);
	}

	void recreateSwapChain() {
		int width = 0, height = 0;
		glfwGetFramebufferSize(window, &width, &height);
		while (width == 0 || height == 0) {
			glfwGetFramebufferSize(window, &width, &height);
			// glfwWaitEvents是GLFW的函数，用于挂起程序的执行，直到发生窗口事件，或者直到窗口的状态发生变化
			glfwWaitEvents();
		}

		vkDeviceWaitIdle(device);
		cleanupSwapChain();

		createSwapChain();
		createImageViews();
		createColorResources();
		createDepthResources();
		createFramebuffers();
	}

	void createCommandBuffers() {
		commandBuffers.resize(MAX_FRAMES_IN_FLIGHT);
		// VkCommandBufferAllocateInfo用于分配命令缓冲区（VkCommandBuffer）的结构体
		// sType 结构体标识
		// commandPool 命令池句柄，指定从哪个命令池分配命令缓冲区
		// level 命令缓冲区的级别，VK_COMMAND_BUFFER_LEVEL_PRIMARY（主命令缓冲区，可以直接提交到队列进行执行）、VK_COMMAND_BUFFER_LEVEL_SECONDARY（次命令缓冲区，不能直接提交到队列，但可以被嵌套在子命令缓冲区中）
		// commandBufferCount 指定要分配的命令缓冲区的数量
		VkCommandBufferAllocateInfo allocInfo{};
		allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
		allocInfo.commandPool = commandPool;
		allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
		allocInfo.commandBufferCount = (uint32_t) commandBuffers.size();
		
		// vkAllocateCommandBuffers用于分配命令缓冲区（Command Buffers）的函数
		if (vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()) != VK_SUCCESS) {
			throw std::runtime_error("failed to allocate command buffers!");
		}
	}

	void recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex) {
		// VkCommandBufferBeginInfo用于指定如何开始记录命令缓冲区（VkCommandBuffer）的命令
		// sType 结构体标识
		// flags 指定如何使用命令缓冲区，VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT（命令缓冲区执行一次后将立即重新记录）、VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT（确保次级命令缓冲区的所有命令会在已经开始的渲染通道内继续执行，不会启动新的渲染通道）、
		// VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT（命令缓冲区可以在等待执行的同时重新提交）
		// pInheritanceInfo 该字段仅在次级命令缓冲区有效，指向一个VkCommandBufferInheritanceInfo结构体，该结构体包含继承自主命令缓冲区的属性（例如，渲染通道状态、帧缓冲等）
		VkCommandBufferBeginInfo beginInfo{};
		beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
		// beginInfo.flags = 0;
		// beginInfo.pInheritanceInfo = nullptr;
		// vkBeginCommandBuffer是用于开始记录命令缓冲区（VkCommandBuffer）命令的函数
		if (vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS) {
			throw std::runtime_error("failed to begin recording command buffer!");
		}
		
		// VkRenderPassBeginInfo是用于开始一个渲染通道（Render Pass）的结构体
		// sType 结构体标识
		// renderPass 渲染通道对象的句柄
		// framebuffer 当前渲染通道使用的帧缓冲区对象
		// renderArea 渲染区域的矩形区域
		// clearValueCount 清除值的数量
		// pClearValues 指向VkClearValue结构体数组的指针，包含了清除操作的值，将颜色定义为指定颜色
		VkRenderPassBeginInfo renderPassInfo{};
		renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
		renderPassInfo.renderPass = renderPass;
		renderPassInfo.framebuffer = swapChainFramebuffers[imageIndex];
		renderPassInfo.renderArea.offset = {0, 0};
		renderPassInfo.renderArea.extent = swapChainExtent;
		
		// VkClearValue是用于表示清除操作的结构体，通常在渲染通道（render pass）开始时，用于指定清除颜色、深度或模板附件的值
		// color 用于颜色缓冲区清除时的颜色值（VkClearColorValue）
		// depthStencil 用于深度和模板缓冲区清除时的值
		VkClearValue clearColor = { {{0.0f, 0.0f, 0.0f, 1.0f}} };
		std::array<VkClearValue, 2> clearValues{};
		clearValues[0].color = { {0.0f, 0.0f, 0.0f, 1.0f} };
		clearValues[1].depthStencil = {1.0f, 0};
		renderPassInfo.clearValueCount = static_cast<uint32_t>(clearValues.size());
		renderPassInfo.pClearValues = clearValues.data();
		
		// vkCmdBeginRenderPass是用于开始一个渲染通道（render pass）的指令
		// VK_SUBPASS_CONTENTS_INLINE（渲染过程命令将嵌入到主命令缓冲区本身中，并且不会执行任何辅助命令缓冲区）
		// VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS（渲染过程命令将从辅助命令缓冲区执行）
		vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);

		// vkCmdBindPipeline用于绑定管线对象（pipeline）的对象，目的是将一个图形或计算管线绑定到当前的命令缓冲区，使得后续的渲染命令可以使用
		// 这个管线进行图形处理或计算操作
		vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline);

		// VkViewport用于定义视口（viewport）的结构体，它指定了如何将裁剪空间（clip space）映射到窗口或帧缓冲区的像素空间 
		// x 视口左上角x坐标
		// y 视口左上角y坐标
		// width 视口的宽度
		// height 视口的高度
		// minDepth 深度值的最小值
		// maxDepth 深度值的最大值
		VkViewport viewport{};
		viewport.x = 0.0f;
		viewport.y = 0.0f;
		viewport.width = static_cast<float>(swapChainExtent.width);
		viewport.height = static_cast<float>(swapChainExtent.height);
		viewport.minDepth = 0.0f;
		viewport.maxDepth = 1.0f;
		
		// vkCmdSetViewport用于在命令缓冲区中设置视口（viewport）的命令
		vkCmdSetViewport(commandBuffer, 0, 1, &viewport);

		// VkRect2D是用于表示二维矩形区域的结构体
		VkRect2D scissor{};
		scissor.offset = { 0,0 };
		scissor.extent = swapChainExtent;

		// vkCmdSetScissor用于设置裁剪区域的命令
		vkCmdSetScissor(commandBuffer, 0, 1, &scissor);

		VkBuffer vertexBuffers[] = {vertexBuffer};
		VkDeviceSize offsets[] = { 0 };
		// vkCmdBindVertexBuffers用于将顶点缓冲区绑定到命令缓冲区的函数
		vkCmdBindVertexBuffers(commandBuffer, 0, 1, vertexBuffers, offsets);
		
		// vkCmdBindIndexBuffer是将索引缓冲区绑定到命令缓冲区的函数
		vkCmdBindIndexBuffer(commandBuffer, indexBuffer, 0, VK_INDEX_TYPE_UINT32);

		vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets[currentFrame], 0, nullptr);

		// vkCmdDraw用于发起图形绘制的函数
		// vkCmdDraw(commandBuffer, static_cast<uint32_t>(vertices.size()), 1, 0, 0);
		
		// vkCmdDrawIndexed用于绘制图形的命令，用于执行基于索引的绘制操作
		vkCmdDrawIndexed(commandBuffer, static_cast<uint32_t>(indices.size()), 1, 0, 0, 0);
		
		// vkCmdEndRenderPass是用于结束当前渲染通道（render pass）的命令
		vkCmdEndRenderPass(commandBuffer);
		
		// vkEndCommandBuffer是用于结束当前命令缓冲区录制的函数
		if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS) {
			throw std::runtime_error("failed to record command buffer!");
		}
	}

	void createCommandPool() {
		QueueFamilyIndices queueFamilyIndices = findQueueFamilies(physicalDevice);
		// VkCommandPoolCreateInfo是用于创建VkCommandPool的结构体
		// sType 结构体标识
		// flags 命令池的标识，用于控制命令池的行为，VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT（表示命令缓冲区可以被重置）、
		// VK_COMMAND_POOL_CREATE_TRANSIENT_BIT（表示命令池只用于短生命周期的命令缓冲区）、VK_COMMAND_POOL_CREATE_FLAG_BITS_NONE（默认值，没有特殊标志）
		// queueFamilyIndex 指定命令池对应的队列族的索引
		VkCommandPoolCreateInfo poolInfo{};
		poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
		poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
		poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value();
		// vkCreateCommandPool是用于创建命令池（VkCommandPool）的函数
		if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) {
			throw std::runtime_error("failed to create command pool!");
		}
	}

	void createFramebuffers() {
		swapChainFramebuffers.resize(swapChainImageViews.size());
		for (size_t i = 0; i < swapChainFramebuffers.size(); ++i) {
			std::array<VkImageView, 3> attachments = {
				colorImageView, 
				depthImageView, 
				swapChainImageViews[i]
			};
			// VkFramebufferCreateInfo是创建VkFramebuffer对象的结构体，包含了有关帧缓冲对象的信息
			// sType 结构体标识
			// renderPass 与该帧缓冲区关联的渲染管线对象
			// attachmentCount 附件的数量，指帧缓冲中图像视图（VkImageView）的数量
			// pAttachments 指向VkImageView数组的指针
			// width 帧缓冲的宽度，通常与附件的图像大小相匹配
			// height 帧缓冲的高度，通常与附件的图像大小相匹配
			// layers 帧缓冲的层数，如果是普通2D渲染，则设置为1；如果是3D渲染或使用多层渲染目标时，可能会大于1
			VkFramebufferCreateInfo framebufferInfo{};
			framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
			framebufferInfo.renderPass = renderPass;
			framebufferInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
			framebufferInfo.pAttachments = attachments.data();
			framebufferInfo.width = swapChainExtent.width;
			framebufferInfo.height = swapChainExtent.height;
			framebufferInfo.layers = 1;

			// vkCreateFramebuffer是创建VkFramebuffer对象的函数
			if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]) != VK_SUCCESS) {
				throw std::runtime_error("failed to create framebuffer!");
			}
		}
	}

	void createRenderPass() {
		// VkAttachmentDescription用于描述渲染目标的属性，通常用于设置帧缓冲（Framebuffer）中的附件
		// format 附件图像的格式，通常是颜色缓冲、深度缓冲或模板缓冲的格式
		// samples 每个像素的样本数量
		// loadOp 渲染开始时的加载操作 VK_ATTACHMENT_LOAD_OP_LOAD（保留当前数据）、VK_ATTACHMENT_LOAD_OP_CLEAR（清空附件内容）、VK_ATTACHMENT_LOAD_OP_DONT_CARE（不关心附件内容）
		// storeOp 渲染完成后的存储操作 VK_ATTACHMENT_STORE_OP_STORE（将渲染结果返回到附件）、VK_ATTACHMENT_STORE_OP_DONT_CARE（不关心保存操作，通常用于不需要保留结果的操作）
		// stencilLoadOp和stencilStoreOp 是针对模板缓冲的加载和缓冲操作
		// initialLayout 定义在渲染开始之前，附件所处的布局状态
		// finalLayout 渲染完成后，该附件所处的布局状态
		VkAttachmentDescription colorAttachment{};
		colorAttachment.format = swapChainImageFormat;
		colorAttachment.samples = msaaSamples;
		colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
		colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
		colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
		colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
		colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
		colorAttachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;

		// VkAttachmentDescription用于描述渲染通道中附件的一种结构体
		// format 附件的图像格式，通常是颜色图像或深度图像的格式
		// samples 附件的样本数，用于指定多重采样的数量
		// loadOp 附件的加载操作，指定渲染开始时如何处理附件数据
		// storeOp 附件的存储操作，指定渲染结束时如何处理附件数据
		// stencilLoadOp 模板附件的加载操作，用于模板缓冲区
		// stencilStoreOp 模板附件的存储操作，用于模板缓冲区
		// initialLayout 图像的初始布局，渲染开始时图像的数据应该处于什么布局
		// finalLayout 图像的最终布局，渲染结束后图像的数据应该处于什么布局
		VkAttachmentDescription depthAttachment{};
		depthAttachment.format = findDepthFormat();
		depthAttachment.samples = msaaSamples;
		depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
		depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
		depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
		depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
		depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
		depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;

		VkAttachmentDescription colorAttachmentResolve{};
		colorAttachmentResolve.format = swapChainImageFormat;
		colorAttachmentResolve.samples = VK_SAMPLE_COUNT_1_BIT;
		colorAttachmentResolve.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
		colorAttachmentResolve.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
		colorAttachmentResolve.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
		colorAttachmentResolve.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
		colorAttachmentResolve.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
		colorAttachmentResolve.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
		
		// VkAttachmentReference用于描述帧缓冲区附件的引用
		// attachment 附件描述数组中的索引指定要引用的附件
		// layout 附件在渲染操作中的图像布局
		VkAttachmentReference colorAttachmentRef{};
		colorAttachmentRef.attachment = 0;
		colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;

		VkAttachmentReference depthAttachmentRef{};
		depthAttachmentRef.attachment = 1;
		depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;

		VkAttachmentReference colorAttachmentResolveRef{};
		colorAttachmentResolveRef.attachment = 2;
		colorAttachmentResolveRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;

		// VkSubpassDescription是描述渲染通道（subpass）的具体配置
		// pipelineBindPoint 指定子通道绑定的管线类型，VK_PIPELINE_BIND_POINT_GRAPHICS（图形管线）、VK_PIPELINE_BIND_POINT_COMPUTE（计算管线）
		// colorAttachmentCount 颜色输出附件数量
		// pColorAttachments 输出颜色附件数组，该数组中附件的索引是使用layout(location = 0) out vec4 outColor指令从片段着色器直接引用的
		// pInputAttachments 从着色器读取的附件
		// pResolveAttachments 用于多重采样颜色附件的附件
		// pDepthStencilAttachment 深度和模板数据的附件
		// pPreserveAttachments 此子通道未使用但必须保留数据的附件
		VkSubpassDescription subpass{};
		subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
		subpass.colorAttachmentCount = 1;
		subpass.pColorAttachments = &colorAttachmentRef;
		subpass.pDepthStencilAttachment = &depthAttachmentRef;
		subpass.pResolveAttachments = &colorAttachmentResolveRef;

		// VkSubpassDependency用于描述两个子通道（subpass）之间的依赖关系的结构体
		// srcSubpass 源子通道的索引，对于第一个子通道，通常是VK_SUBPASS_EXTERNAL，表示外部操作，特殊值VK_SUBPASS_EXTERNAL指的是渲染通道之前或之后的隐式子通道
		// dstSubpass 目标子通道的索引
		// srcStageMask 源子通道依赖的管线阶段
		// srcAccessMask 源子通道所需的内存访问权限
		// dstStageMask 目标子通道依赖的管线阶段
		// dstAccessMask 目标子通道所需的内存访问权限
		VkSubpassDependency dependency{};
		dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
		dependency.dstSubpass = 0;
		dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
		dependency.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
		dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
		dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;

		std::array<VkAttachmentDescription, 3> attachments = {colorAttachment, depthAttachment, colorAttachmentResolve};
		// VkRenderPassCreateInfo是创建渲染通道（VkRenderPass）的关键结构体
		// sType 结构体标识
		// attachementCount 附件数量
		// pAttachments 附件描述数组
		// subpassCount 子通道数量
		// pSubpasses 子通道描述数组
		// dependencyCount 子通道之间的依赖关系数量
		// pDependencies 指向VkSubpassDependency数组的指针
		VkRenderPassCreateInfo renderPassInfo{};
		renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
		renderPassInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
		renderPassInfo.pAttachments = attachments.data();
		renderPassInfo.subpassCount = 1;
		renderPassInfo.pSubpasses = &subpass;
		renderPassInfo.dependencyCount = 1;
		renderPassInfo.pDependencies = &dependency;

		// vkCreateRenderPass是创建渲染通道（Render Pass）的核心函数
		if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) {
			throw std::runtime_error("failed to create render pass!");
		}
	}

	void createGraphicsPipeline() {
		auto vertShaderCode = readFile("shaders/vert.spv");
		auto fragShaderCode = readFile("shaders/frag.spv");

		VkShaderModule vertShaderModule = createShaderModule(vertShaderCode);
		VkShaderModule fragShaderModule = createShaderModule(fragShaderCode);

		// 为了实际使用着色器，我们需要通过VkPipelineShaderStageCreateInfo结构将它们分配到特定的管道阶段，
		// 作为实际管道创建过程的一部分
		
		// VkPipelineShaderStageCreateInfo是创建渲染管线（vkCreateGrphicsPipelines）时指定每个着色器阶段信息的结构体，
		// 它告诉驱动：哪个阶段用哪个VkShaderModule、入口函数名是什么、是否有特定的常量或特性等
		// sType 结构体标识
		// stage 着色器阶段标识
		// module 要使用的VkShaderModule句柄
		// pName 着色器入口函数名
		VkPipelineShaderStageCreateInfo vertShaderStageInfo{};
		vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
		vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
		vertShaderStageInfo.module = vertShaderModule;
		vertShaderStageInfo.pName = "main";

		VkPipelineShaderStageCreateInfo fragShaderStageInfo{};
		fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
		fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
		fragShaderStageInfo.module = fragShaderModule;
		fragShaderStageInfo.pName = "main";

		VkPipelineShaderStageCreateInfo shaderStages[] = {vertShaderStageInfo, fragShaderStageInfo};
		

		// VkPipelineVertexInputStateCreateInfo是用来描述顶点输入阶段（Vertex Input State）数据格式的结构体
		// vertexBindingDescriptionCount/pVertexBindingDescriptions 标识缓冲的步幅和绑定号
		// vertexAttributeDescriptionCount/pVertexAttributeDescriptions 标识属性在缓冲里偏移和格式
		VkPipelineVertexInputStateCreateInfo vertexInputInfo{};
		vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;

		auto bindingDescription = Vertex::getBindingDescription();
		auto attributeDescriptions = Vertex::getAttributeDescriptions();

		vertexInputInfo.vertexBindingDescriptionCount = 1;
		vertexInputInfo.pVertexBindingDescriptions = &bindingDescription;
		vertexInputInfo.vertexAttributeDescriptionCount = static_cast<uint32_t>(attributeDescriptions.size());
		vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data();

		// VkPipelineInputAssemblyStateCreateInfo 是图像管线（Graphics Pipeline）中输入装配阶段（Input Assembly Stage）的配置结构体
		// topology 核心字段，决定了顶点如何组成图元
		// VK_PRIMITIVE_TOPOLOGY_POINT_LIST 每个顶点绘制一个点
		// VK_PRIMITIVE_TOPOLOGY_LINE_LIST 每两个顶点组成一条线
		// VK_PRIMITIVE_TOPOLOGY_LINE_STRIP 每个顶点与前一个顶点组成线
		// VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST 每三个顶点组成三角形
		// VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP 从第三个顶点开始，每三个顶点组成一个三角形，且共享前两个顶点
		// VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN 从第一个顶点为中心，形成扇形三角形
		// primitiveRestartEnable 是否重启图元，说白了是否重新绘制图元
		VkPipelineInputAssemblyStateCreateInfo inputAssembly{};
		inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
		inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
		inputAssembly.primitiveRestartEnable = VK_FALSE;
		
		// VkPieplineViewportStateCreateInfo是图像管线创建时用于配置视口（Viewport）和裁剪矩形（Scissor）的结构体
		// sType 结构体标识
		// viewportCount 视口数量，通常为1
		// scissorCount 裁剪矩形，必须与viewportCount一致
		VkPipelineViewportStateCreateInfo viewportState{};
		viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
		viewportState.viewportCount = 1;
		viewportState.scissorCount = 1;

		// VkPipelineRasterizationStateCreateInfo是渲染管线中用于配置光栅化阶段（Rasterization Stage）的核心结构体之一
		// sType 结构体标识
		// depthClampEnable 是否启动深度钳制；如果启动，如果某个片段的深度超出了[0,1]，不要裁剪掉，而是自动钳制（clamp）到这个范围，
		// 如果不启动，如果某个片段的深度超出了[0,1]，会裁剪掉
		// rasterizerDiscardEnable 是否禁用光栅化，如果启动，则禁用光栅化，则不会执行.frag文件；实际上会禁用任何到帧缓冲区的输出
		// polygonMode 控制绘制多边形的方式；VK_POLYGON_MODE_FILL 默认值，绘制实心多边形、VK_POLYGON_MODE_LINE 绘制线框（Wireframe）、VK_POLYGON_MODE_POINT 绘制顶点为点
		// lineWidth 线宽，通常为1.0f，若要使用非1.0的宽度，需启用GPU功能wideLines
		// cullMode 控制剔除那些面（背面，正面，两者都不剔）
		// frontFace 指定那个顶点顺序为正面，VK_FRONT_FACE_COUNTER_CLOCKWISE（默认值）逆时针，VK_FRONT_FACE_CLOCKWISE顺时针
		// depthBiasEnable 是否启动深度偏移
		// depthBiasConstantFactor 常量偏移量，常量值，直接加到片段的深度值上
		// depthBiasClamp 钳制偏移后的深度值，默认值0.0f，表示没有钳制
		// depthBiasSlopeFactor 表示深度偏移的斜率因子，默认值0.0f，表示不使用斜率偏移
		VkPipelineRasterizationStateCreateInfo rasterizer{};
		rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
		rasterizer.depthClampEnable = VK_FALSE;
		rasterizer.rasterizerDiscardEnable = VK_FALSE;
		rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
		rasterizer.lineWidth = 1.0f;
		rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
		rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
		rasterizer.depthBiasEnable = VK_FALSE;
		// rasterizer.depthBiasConstantFactor = 0.0f;
		// rasterizer.depthBiasClamp = 0.0f;
		// rasterizer.depthBiasSlopeFactor = 0.0f;

		// VkPipelineMultisampleStateCreateInfo是渲染管线中用于设置多重采样（Multisampling）阶段的配置结构体，它在管线创建时
		// 定义了如何处理抗锯齿
		// sType 结构体标识
		// sampleShadingEnable 是否启用样品平滑
		// rasterizationSamples 指定多重采样的数量，VK_SAMPLE_COUNT_1_BIT（不使用多重采样）
		// VK_SAMPLE_COUNT_2_BIT（2X多重采样）、VK_SAMPLE_COUNT_4_BIT（4X多重采样）、VK_SAMPLE_COUNT_8_BIT（8X多重采样）
		// VK_SAMPLE_COUNT_16_BIT（16X多重采样）、VK_SAMPLE_COUNT_32_BIT（32X多重采样）、VK_SAMPLE_COUNT_64_BIT（64X多重采样）
		// minSampleShading 样本平滑阈值
		// pSampleMask 指向样本掩码的指针
		// alphaToCoverageEnable 是否启用alpha测试覆盖
		// alphaToOneEnable 是否将alpha值设置为1.0
		VkPipelineMultisampleStateCreateInfo multisampling{};
		multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
		multisampling.sampleShadingEnable = VK_TRUE;
		multisampling.minSampleShading = .2f;
		multisampling.rasterizationSamples = msaaSamples;
		// multisampling.minSampleShading = 1.0f;
		// multisampling.pSampleMask = nullptr;
		// multisampling.alphaToCoverageEnable = VK_FALSE;
		/// multisampling.alphaToOneEnable = VK_FALSE;
		
		// VkPipelineDepthStencilStateCreateInfo用于描述深度和模板测试状态的结构体
		// sType 结构体标识
		// depthTestEnable 控制是否启用深度测试
		// depthWriteEanble 是否允许写入深度缓冲区
		// depthCompareOp 指定深度测试的比较操作
		// depthBoundsTestEnable 控制是否启用深度边界测试
		// minDepthBounds、maxDepthBounds 设置深度边界的最大值和最小值
		// stencilTestEnable 是否启用模板测试
		// front、back 定义模板操作状态，分别指定前面和后面面片的模板操作
		VkPipelineDepthStencilStateCreateInfo depthStencil{};
		depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
		depthStencil.depthTestEnable = VK_TRUE;
		depthStencil.depthWriteEnable = VK_TRUE;
		depthStencil.depthCompareOp = VK_COMPARE_OP_LESS;
		depthStencil.depthBoundsTestEnable = VK_FALSE;
		// depthStencil.minDepthBounds = 0.0f;
		// depthStencil.maxDepthBounds = 1.0f;
		depthStencil.stencilTestEnable = VK_FALSE;
		// depthStencil.front = {};
		// depthStencil.back = {};

		// VkPipelineColorBlendAttachmentState是用于描述每个渲染目标（渲染管线的输出颜色附件）如何进行颜色混合（Color Blending）的结构体
		// blendEnable 是否启用颜色混合
		// srcColorBlendFactor 指定源颜色（即当前片段的颜色）和目标颜色（即渲染目标的当前颜色）进行混合时，源颜色的权重因子
		// VK_BLEND_FACTOR_ZERO（权重0）、VK_BLEND_FACTOR_ONE（权重1）、VK_BLEND_FACTOR_SRC_COLOR（源颜色的分量）、VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR（1-源颜色的分量）
		// VK_BLEND_FACTOR_DST_COLOR（目标颜色的分量）
		// dstColorBlendFactor 指定目标颜色与源颜色进行混合时，目标颜色的权重因子
		// colorBlendOp 指定颜色混合操作，即源颜色和目标颜色的混合方式
		// srcAlphaBlendFactor 指定源alpha值（即当前片段的alpha值）与目标alpha值（即渲染目标的alpha值）进行混合时，源alpha的权重因子
		// dstAlphaBlendFactor 指定目标alpha值和源alpha值进行混合时，目标alpha的权重因子
		// alphaBlendOp 指定alpha混合操作，即源alpha值和目标alpha值的混合方式
		// colorWriteMask 控制颜色的那些分量可以写入目标附件
		VkPipelineColorBlendAttachmentState colorBlendAttachment{};
		colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
			VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
		colorBlendAttachment.blendEnable = VK_FALSE;
		// colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
		// colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO;
		// colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD;
		// colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
		// colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
		// colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD;

		// VkPipelineColorBlendStateCreateInfo是用于配置渲染管线颜色混合（Color Blending）状态的结构体
		// sType 结构体标识
		// logicOpEnable 表示是否启用逻辑操作
		// logicOp 指定使用的逻辑操作类型，VK_LOGIC_OP_COPY（复制源颜色）、VK_LOGIC_OP_AND（按位与）、VK_LOGIC_OP_OR（按位或）
		// attachmentCount 渲染目标附件的数量，即有多少个颜色附件需要进行混合操作
		// pAttachments 指向一个数组的的指针，数组中的每个元素定一个颜色附件的混合操作
		// blendConstants 存储用于颜色混合的常量
		VkPipelineColorBlendStateCreateInfo colorBlending{};
		colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
		colorBlending.logicOpEnable = VK_FALSE;
		colorBlending.logicOp = VK_LOGIC_OP_COPY;
		colorBlending.attachmentCount = 1;
		colorBlending.pAttachments = &colorBlendAttachment;
		colorBlending.blendConstants[0] = 0.0f;
		colorBlending.blendConstants[1] = 0.0f;
		colorBlending.blendConstants[2] = 0.0f;
		colorBlending.blendConstants[3] = 0.0f;

		// VkDynamicState是用来描述渲染管线中那些状态是“动态”的
		std::vector<VkDynamicState> dynamicStates = {
			VK_DYNAMIC_STATE_VIEWPORT,  // 视口矩形动态设置
			VK_DYNAMIC_STATE_SCISSOR		// 裁剪矩形动态设置
		};
		
		// VkPipelineDynamicStateCreateInfo是用来描述哪些状态在管线创建时不固定、在渲染时动态设置的结构体
		// sType 结构体标识
		// dynamicStateCount 动态状态数量
		// pDynamicStates 执行VkDynamicState枚举数组
		VkPipelineDynamicStateCreateInfo dynamicState{};
		dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
		dynamicState.dynamicStateCount = static_cast<uint32_t>(dynamicStates.size());
		dynamicState.pDynamicStates = dynamicStates.data();
		
		// VkPipelineLayoutCreateInfo是用于创建管线布局（Pipeline Layout）的结构体 
		// sType 结构体标识
		// setLayoutCount 描述符集布局的数量
		// pSetLayouts 指向VkDescriptorSetLayout数组的指针
		// pushConstantRangeCount push常量的数量，push常量用于在着色器中传递小量数据
		// pPushConstantRanges 指向VkPushConstantRange数组的指针
		VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
		pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
		pipelineLayoutInfo.setLayoutCount = 1;
		pipelineLayoutInfo.pSetLayouts = &descriptorSetLayout;
		// pipelineLayoutInfo.pushConstantRangeCount = 0;
		// pipelineLayoutInfo.pPushConstantRanges = nullptr;
		
		// vkCreatePipelineLayout是用于创建管线布局（Pipeline Layout）的函数
		if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) {
			throw std::runtime_error("failed to create pipeline layout!");
		}
		
		// VkGraphicsPipelineCreateInfo是用于创建图形管线（Graphics Pipeline）的结构体
		// sType 结构体标识
		// stageCount 管线中包含的着色器阶段数量，通常至少是2（顶点着色器和片段着色器）
		// pStages 数组，包含管线中所有着色器阶段信息
		// pVertexInputState 描述顶点输入的结构体，定义了输入的顶点数据格式
		// pInputAssemblyState 输入装配状态，定义了如何将顶点数据组装成图元
		// pViewportState 视口和裁剪区域的配置
		// pRasterizationState 光栅化状态，定义了图元如何被光栅化为像素
		// pMultisampleState 多重采样配置，例如抗锯齿设置
		// pDepthStencilState 深度和模板测试状态
		// pColorBlendState 颜色混合状态，用于控制多个渲染目标的合成方式
		// pDynamicState 动态状态，控制那些状态可以在绘制过程中动态更改
		// layout 管线布局，用于着色器与资源之间的绑定
		// renderpPass 渲染通道，定义了该管线在那个渲染通道中运行
		// subpass 指定管线执行的子通道，通常是0
		// basePipelineHandle 用于创建派生管线时的基础管线句柄，通常为（VK_NULL_HANDLE）
		// basePipelineIndex 基础管线的索引，通常为-1
		VkGraphicsPipelineCreateInfo pipelineInfo{};
		pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
		pipelineInfo.stageCount = 2;
		pipelineInfo.pStages = shaderStages;
		pipelineInfo.pVertexInputState = &vertexInputInfo;
		pipelineInfo.pInputAssemblyState = &inputAssembly;
		pipelineInfo.pViewportState = &viewportState;
		pipelineInfo.pRasterizationState = &rasterizer;
		pipelineInfo.pMultisampleState = &multisampling;
		// pipelineInfo.pDepthStencilState = nullptr;
		pipelineInfo.pColorBlendState = &colorBlending;
		pipelineInfo.pDynamicState = &dynamicState;
		pipelineInfo.layout = pipelineLayout;
		pipelineInfo.renderPass = renderPass;
		pipelineInfo.subpass = 0;
		pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
		pipelineInfo.pDepthStencilState = &depthStencil;
		// pipelineInfo.basePipelineIndex = -1;

		// vkCreateGraphicsPipelines是创建图像渲染管线的函数
		if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) {
			throw std::runtime_error("failed to create graphics pipeline!");
		}
		
		// vkDestroyShaderModule是用来销毁VkShaderModule对象的函数
		vkDestroyShaderModule(device, fragShaderModule, nullptr);
		vkDestroyShaderModule(device, vertShaderModule, nullptr);
	}
	
	// VkShaderModule是着色器的封装对象，包含编译好的SPIR-V二进制代码
	// VkShaderModuleCreateInfo	是用于创建着色器模块（VkShaderModule）时所需的结构体
	// sType 结构体标识
	// pCode SPIR-V字节码数组
	// codeSize 字节码长度
	// 字节码的长度是以字节（bytes）为单位，但字节码指针的类型是uint32_t*而不是char*。因此我们需要使用reinterpret_cast进行类型转换，
	// 需要确保数据满足uint32_t的对齐要求（alignment requirements），数据存储在std::vector中，
	// 而默认的分配器已经保证了数据满足最坏情况下的对齐要求
	VkShaderModule createShaderModule(const std::vector<char>& code) {
		VkShaderModuleCreateInfo createInfo{};
		createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
		createInfo.codeSize = code.size();
		createInfo.pCode = reinterpret_cast<const uint32_t*>(code.data());
		VkShaderModule shaderModule;
		// vkCreateShaderModule是用来根据SPIR-V字节码创建着色器模块的函数
		if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) {
			throw std::runtime_error("failed to create shader module!");
		}
		// 着色器模块只是对我们之前从文件加载的着色器字节码及其中定义的函数的薄包装， 
		// SPIR-V字节码的编译和链接到GPU执行的机器码，直到图形管线创建完成才会发生，
		// 当管线创建完成之后，管线对象内部持有了自己需要的Shader副本
		return shaderModule;
	}

	void createImage(
		uint32_t width, 
		uint32_t height, 
		uint32_t mipLevels, 
		// VkSampleCountFlagBits用于描述图像样本数（抗锯齿级别）的标志位
		VkSampleCountFlagBits numSamples, 
		// VkFormat用于描述图像或缓冲区数据格式的枚举类型
		VkFormat format, 
		// VkImageTiling用于指定图像存储布局的枚举类型
		VkImageTiling tiling, 
		// VkImageUsageFlags是位掩码，用于描述图像在Vulkan中的用途或用途集合
		VkImageUsageFlags usage, 
		// VkMemoryPropertyFlags用于描述内存属性的标志，定义了内存块的特性或行为
		VkMemoryPropertyFlags properties, 
		VkImage& image, 
		VkDeviceMemory& imageMemory) {
		// VkImageCreateInfo用于描述图像（VkImage）创建信息的结构体
		// sType 结构体标识
		// imageType 指向图像的类型
		// extent 图像的尺寸
		// mipLevels mipmap级别的数量
		// arrayLayouts 图像的数组层数
		// format 图像的格式，用于描述图像的颜色深度、颜色组件等
		// tiling 图像的内存布局类型
		// initialLayout 图像的初始布局，指示图像在创建时的布局状态
		// usage 图像的用途
		// samples 采样数，指图像的抗锯齿级别
		// sharingMode 指图像的共享模式
		VkImageCreateInfo imageInfo{};
		imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
		imageInfo.imageType = VK_IMAGE_TYPE_2D;
		imageInfo.extent.width = width;
		imageInfo.extent.height = height;
		imageInfo.extent.depth = 1;
		imageInfo.mipLevels = mipLevels;
		imageInfo.arrayLayers = 1;
		imageInfo.format = format;
		imageInfo.tiling = tiling;
		imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
		imageInfo.usage = usage;
		imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
		imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
		imageInfo.samples = numSamples;

		// vkCreateImage用于创建图像对象（VkImage）
		if (vkCreateImage(device, &imageInfo, nullptr, &image) != VK_SUCCESS) {
			throw std::runtime_error("failed to create image!");
		}

		VkMemoryRequirements memRequirements;
		// vkGetImageMemoryRequirements用于查询图像对象的内存要求（如内存大小、对齐方式和可用的内存类型）
		vkGetImageMemoryRequirements(device, image, &memRequirements);
	
		VkMemoryAllocateInfo allocInfo{};
		allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
		allocInfo.allocationSize = memRequirements.size;
		allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties);

		if (vkAllocateMemory(device, &allocInfo, nullptr, &imageMemory) != VK_SUCCESS) {
			throw std::runtime_error("failed to allocate image memory!");
		}
		
		// vkBindImageMemory用于将一个已经分配的内存对象绑定到一个图像对象（VkImage）上
		vkBindImageMemory(device, image, imageMemory, 0);
	}

	void createImageViews() {
		swapChainImageViews.resize(swapChainImages.size());
		// for (size_t i = 0; i < swapChainImageViews.size(); ++i) {
		for (size_t i = 0; i < swapChainImages.size(); ++i) {
			// // VkImageViewCreateInfo是用于创建图像视图的结构体
			// // sType 结构体标识
			// // image VkImage对象
			// // viewType 图像类型，图像视为1D纹理、2D纹理、3D纹理和立方体贴图
			// // format 图像格式，必须和image匹配
			// // subresourceRange.aspectMask 指定视图包含的图像方面，VK_IMAGE_ASPECT_COLOR_BIT（颜色）、VK_IMAGE_ASPECT_DEPTH_BIT（深度）、VK_IMAGE_ASPECT_STENCIL_BIT（模板）
			// // subresourceRange.baseMpiLevel 视图包含的第一个mip层（从0开始）
			// // subresourceRange.levelCount 包含的mip层数量
			// // subresourceRange.baseArrayLayer 第一个ArrayLayer索引	
			// // subresourceRange.layerCount 包含的ArrayLayer数量
			// VkImageViewCreateInfo createInfo{};
			// createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
			// createInfo.image = swapChainImages[i];
			// createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
			// createInfo.format = swapChainImageFormat;
			// createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
			// createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
			// createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
			// createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
			// createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
			// createInfo.subresourceRange.baseMipLevel = 0;
			// createInfo.subresourceRange.levelCount = 1;
			// createInfo.subresourceRange.baseArrayLayer = 0;
			// createInfo.subresourceRange.layerCount = 1;
			// 
			// // vkCreateImageView 用于创建图像视图的函数
			// if (vkCreateImageView(device, &createInfo, nullptr, &swapChainImageViews[i]) != VK_SUCCESS) {
			// 	throw std::runtime_error("failed to create image views!");
			// }

			swapChainImageViews[i] = createImageView(swapChainImages[i], swapChainImageFormat, VK_IMAGE_ASPECT_COLOR_BIT, 1);
		}
	}

	void createSwapChain() {
		SwapChainSupportDetails swapChainSupport = querySwapChainSupport(physicalDevice);

		VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats);

		VkPresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes);

		VkExtent2D extent = chooseSwapExtent(swapChainSupport.capabilities);
		// 之所以+1是为了避免出现流水线停顿（Pipeline stall）
		uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1;
		if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount) {
			imageCount = swapChainSupport.capabilities.maxImageCount;
		}
		// VkSwapchainCreateInfoKHR是创建交换链（Swapchain）的核心结构体
		// surface 交换链绑定的窗口表面
		// minImageCount 交换链中最小的图像数量
		// imageColorSpace 图像的颜色空间
		// imageFormat 图像的像素格式
		// imageExtent 交换链的图像大小
		// imageArrayLayers 图像层数
		// imageUsage 图像用途标志VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT，表示图像将用作渲染目标
		// imageSharingMode 图像在多个队列族之间的访问方式，VK_SHARING_MODE_EXCLUSIVE（专用模式，性能最好）
		// VK_SHARING_MODE_CONCURRENT（多个队列族共享）
		// pQueueFamilyIndices 指定共享交换链图像的队列族索引
		// preTransform 图像在显示前的变换
		// compositeAlpha alpha混合方式，常用于透明窗口，VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR（不透明，最常用）
		// clipped 表示忽略不可见区域
		// oldSwapchain 当交换链需要重建（窗口 resize）时，传入旧 swapchain
		VkSwapchainCreateInfoKHR createInfo{};
		createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
		createInfo.surface = surface;
		createInfo.minImageCount = imageCount;
		createInfo.imageFormat = surfaceFormat.format;
		createInfo.imageColorSpace = surfaceFormat.colorSpace;
		createInfo.imageExtent = extent;
		createInfo.imageArrayLayers = 1;
		createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;

		QueueFamilyIndices indices = findQueueFamilies(physicalDevice);
		uint32_t queueFamilyIndices[] = {indices.graphicsFamily.value(), indices.presentFamily.value()};
		if (indices.graphicsFamily != indices.presentFamily) {
			createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
			createInfo.queueFamilyIndexCount = 2;
			createInfo.pQueueFamilyIndices = queueFamilyIndices;
		}
		else {
			createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
			// createInfo.queueFamilyIndexCount = 0;
			// createInfo.pQueueFamilyIndices = nullptr;
		}

		createInfo.preTransform = swapChainSupport.capabilities.currentTransform;
		createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
		createInfo.presentMode = presentMode;
		createInfo.clipped = VK_TRUE;
		// createInfo.oldSwapchain = VK_NULL_HANDLE;

		// vkCreateSwapchainKHR是创建交换链的核心函数
		if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) {
			throw std::runtime_error("failed to create swap chain!");
		}

		// vkGetSwapchainImagesKHR用来获取某个交换链内部的图像数组
		vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr);

		swapChainImages.resize(imageCount);

		vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data());

		swapChainImageFormat = surfaceFormat.format;
		swapChainExtent = extent;
	}

	void createSurface() {
		// glfwCreateWindowSurface是GLFW提供的跨平台辅助函数，
		// 用来在Vulkan中直接为GLFW窗口创建一个VkSurfaceKHR表面
		if(glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS){
			throw std::runtime_error("failed to create window surface!");
		}
	}

	void createInstance() {
		if (enableValidationLayers && !checkValidationLayerSupport()) {
			throw std::runtime_error("validation layers requested, but not available!");
		}
		// VkApplicationInfo是辅助结构体，用来描述应用程序、引擎及目标Vulkan API版本等信息
		// sType 结构体标识
		// pNext 链式扩展指针
		// pApplicationName 应用程序名称
		// applicationVersion 应用程序版本
		// pEngineName 引擎名称
		// engineVersion 引擎版本
		// apiVersion 目标Vulkan API版本
		VkApplicationInfo appInfo{};
		appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
		appInfo.pApplicationName = "Hello Triangle";
		appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
		appInfo.pEngineName = "No Engine";
		appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
		appInfo.apiVersion = VK_API_VERSION_1_0;
		
		// VkInstanceCreateInfo是创建Vulkan实例（VkInstance）时必须填写的结构体
		// sType 结构体标识
		// pNext 链式扩展指针
		// flags 预留字段，暂时设置为0
		// pApplicationInfo 指向VkApplicationInfo
		// enabledLayerCount 需要启用的验证层数量
		// ppEnabledLayerNames 需要启用的验证层名称
		// enabledExtensionCount 要启用的扩展数量
		// ppEnabledExtensionNames 要启用的名字数组
		VkInstanceCreateInfo createInfo{};
		createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
		createInfo.pApplicationInfo = &appInfo;

		// glfwGetRequiredInstanceExtensions是GLFW专门为Vulkan提供的函数，它能返回创建VkInstance
		// 时必须启用的平台相关扩展列表
		// glfwExtensionCount是返回的扩展数量
		// glfwExtensions是返回的扩展的数组
		auto extensions = getRequiredExtensions();
		createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
		createInfo.ppEnabledExtensionNames = extensions.data();

		// debugCreateInfo变量被放置在if语句外部，以确保它在调用vkCreateInstance之前不会被销毁，
		// 通过这种方式创建一个额外的调试消息处理器，它将在vkCreateInstance和vkDestroyInstance期间自动使用，并在之后被清理

		// VkDebugUtilsMessengerCreateInfoEXT是创建调试信息处理器（VkDebugUtilsMessengerEXT）的结构体
		VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo{};
		if (enableValidationLayers) {
			createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
			createInfo.ppEnabledLayerNames = validationLayers.data();
			populateDebugMessengerCreateInfo(debugCreateInfo);
			createInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT*)&debugCreateInfo; 
		}
		else {
			createInfo.enabledLayerCount = 0;
			createInfo.pNext = nullptr;
		}

		// 检查扩展支持
		uint32_t availableExtensionCount = 0;
		// vkEnumerateInstanceExtensionProperties用来检索支持的扩展列表的函数
		vkEnumerateInstanceExtensionProperties(nullptr, &availableExtensionCount, nullptr);

		std::vector<VkExtensionProperties> availableExtensions(availableExtensionCount);

		vkEnumerateInstanceExtensionProperties(nullptr, &availableExtensionCount, availableExtensions.data());

		std::cout << "available extensions:\n";

		for (const auto& extension : availableExtensions) {
			std::cout << '\t' << extension.extensionName << '\n';
		}
		
		// vkCreateInstance是创建Vulkan实例（VkInstance）的核心函数，一切Vulkan API操作都必须基于它返回的实例
		if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
			throw std::runtime_error("failed to create instance!");
		}
	}

	SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device) {
		SwapChainSupportDetails details;
		// vkGetPhysicalDeviceSurfaceCapailitiesKHR用来查询物理设备对某个Surface的能力的函数
		vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities);
		
		uint32_t formatCount;
		// vkGetPhysicalDeviceSurfaceFormatsKHR用来查询某个物理设备支持的表面（surface）格式的函数
		vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr);
		if (formatCount != 0) {
			details.formats.resize(formatCount);
			vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data());
		}

		uint32_t presentModeCount;
		// vkGetPhysicalDeviceSurfacePresentModesKHR用于查询物理设备对指定表面支持的呈现模式
		vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr);
		if (presentModeCount != 0) {
			details.presentModes.resize(presentModeCount);
			vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, details.presentModes.data());
		}
		return details;
	}

	void createLogicalDevice() {
		QueueFamilyIndices indices = findQueueFamilies(physicalDevice);
		std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
		std::set<uint32_t> uniqueQueueFamilies = {
			indices.graphicsFamily.value(),
			indices.presentFamily.value()
		};

		float queuePriority = 1.0f;
		for (uint32_t queueFamily : uniqueQueueFamilies) {
			VkDeviceQueueCreateInfo queueCreateInfo{};
			queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
			queueCreateInfo.queueFamilyIndex = queueFamily;
			queueCreateInfo.queueCount = 1;
			queueCreateInfo.pQueuePriorities = &queuePriority;
			queueCreateInfos.push_back(queueCreateInfo);
		}
		
		// VkDeviceQueueCreateInfo是描述逻辑设备中要创建那些队列的结构体
		// sType 结构体标识
		// queueFamilyIndex 队列族索引
		// queueCount 创建的队列数量
		// pQueuePriorities 设置队列优先级数组，0.0~1.0之间的浮点数来影响命令缓冲区执行的调度，
		// 这里本质上是传入的优先级数组的指针
		// VkDeviceQueueCreateInfo queueCreateInfo{};
		// queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
		// queueCreateInfo.queueFamilyIndex = indices.graphicsFamily.value();
		// queueCreateInfo.queueCount = 1;
		// float queuePriority = 1.0f;
		// queueCreateInfo.pQueuePriorities = &queuePriority;
		
		// VkPhysicalDeviceFeatures是描述物理设备（GPU）支持的功能特性的结构体
		// samplerAnisotropy 是否支持纹理采样时的各项异性过滤
		VkPhysicalDeviceFeatures deviceFeatures{};
		deviceFeatures.samplerAnisotropy = VK_TRUE;

		// VkDeviceCreateInfo 是创建逻辑设备（VkDevice）时要传给vkCreateDevice的主结构体
		// sType 结构体标识
		// pQueueCreateInfos 指向VkDeviceQueueCreateInfo数组
		// queueCreateInfoCount 队列创建信息的数量
		// pEnabledFeatures 要启用的物理设备特性（VkPhysicalDeviceFeatures）
		// 早期的Vulkan实现曾经区分过实例级和设备级的验证层，但现在已经不再区分了。
		// 这意味着在最新的实现中，VkDeviceCreateInfo的enabledLayerCount和ppEnabledLayerNames字段会被忽略。
		// 不过，出于兼容旧实现的考虑，仍然建议填写这些字段
		// enabledLayerCount 启用的设备层的数量
		// ppEnabledLayerNames 指向设备层名字的数组的指针
		VkDeviceCreateInfo createInfo{};
		createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
		createInfo.pQueueCreateInfos = queueCreateInfos.data();
		createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size());
		createInfo.pEnabledFeatures = &deviceFeatures;

		createInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size());
		createInfo.ppEnabledExtensionNames = deviceExtensions.data();

		if (enableValidationLayers) {
			createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
			createInfo.ppEnabledLayerNames = validationLayers.data();
		}
		else {
			createInfo.enabledLayerCount = 0;
		}
		// vkCreateDevice是创建逻辑设备（VkDevice）的核心函数
		if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) {
			throw std::runtime_error("failed to create logical device!");
		}

		// vkGetDeviceQueue是从逻辑设备获取队列句柄的函数
		vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue);
		vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue);
 }

	void setupDebugMessenger() {
		if (!enableValidationLayers)return;
		VkDebugUtilsMessengerCreateInfoEXT createInfo;
		populateDebugMessengerCreateInfo(createInfo);
		// CreateDebugUtilsMessengerEXT是扩展函数，用来创建一个调试信息处理器（debug messenger）
		if (CreateDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &debugMessenger) != VK_SUCCESS) {
			throw std::runtime_error("failed to set up debug messenger!");
		}
	}

	//	检查物理设备
	void pickPhysicalDevice() {
		uint32_t deviceCount = 0;
		// vkEnumeratePhysicalDevices用于枚举系统上所有可用的物理设备（即GPU）的方法
		vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
		if (deviceCount == 0) {
			throw std::runtime_error("failed to find GPUs with Vulkan support!");
		}
		std::vector<VkPhysicalDevice> devices(deviceCount);
		vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());

		std::cout << "available physical devices:\n";

		for (const auto& device : devices) {
			VkPhysicalDeviceProperties deviceProperties;
			// vkGetPhysicalDeviceProperties是用于获取物理设备（GPU）属性的函数
			vkGetPhysicalDeviceProperties(device, &deviceProperties);
			std::cout << '\t' << deviceProperties.deviceName << '\n';
		}

		for (const auto& device : devices) {
			if (isDeviceSuitable(device)) {
				physicalDevice = device;
				msaaSamples = getMaxUsableSampleCount();
				break;
			}
		}

		if (physicalDevice == VK_NULL_HANDLE) {
			throw std::runtime_error("failed to find to suitable GPU!");
		}
	}

	// 检查显卡是否合适
	bool isDeviceSuitable(VkPhysicalDevice device) {
		QueueFamilyIndices indices = findQueueFamilies(device);
		bool extensionsSupported = checkDeviceExtensionSupport(device);

		bool swapChainAdequate = false;
		if (extensionsSupported) {
			SwapChainSupportDetails swapChainSupport = querySwapChainSupport(device);
			swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty();
		}
		
		VkPhysicalDeviceFeatures supportedFeatures;
		// vkGetPhysicalDeviceFeatures用于查询某个物理设备支持的特性
		vkGetPhysicalDeviceFeatures(device, &supportedFeatures);

		return indices.isComplete() && extensionsSupported && swapChainAdequate && supportedFeatures.samplerAnisotropy;
	}

	VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats) {
		for (const auto& availableFormat : availableFormats) {
			if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
				return availableFormat;
			}
		}
		return availableFormats[0];
	}

	VkPresentModeKHR chooseSwapPresentMode(const std::vector<VkPresentModeKHR>& availablePresentModes) {
		for (const auto& availablePresentMode : availablePresentModes) {
			if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) {
				return availablePresentMode;
			}
		}

		return VK_PRESENT_MODE_FIFO_KHR;
	}
	
	// 交换范围是交换链图像的分辨率，它几乎总是与要绘制的窗口的分辨率完全相等
	// VkExtent2D用于描述二维尺寸，通常用于交换链（SwapChain）和图像（image）的分辨率、视口（Viewport）大小等场景
	VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) {
		// 如果currentExtent.width != UINT32_MAX，则直接用currentExtent.width，
		// 如果currentExtent.width == UINT32_MAX，则证明需要在minImageExtent／maxImageExtent，则glfwGetFramebufferSize这个返回值
		if (capabilities.currentExtent.width != std::numeric_limits<uint32_t>::max()) {
			return capabilities.currentExtent;
		}
		else {
			int width, height;
			// glfwGetFramebufferSize是GLFW函数，用来获取指定窗口在像素维度上的帧缓冲区的大小（framebuffer size）
			glfwGetFramebufferSize(window, &width, &height);
			VkExtent2D actualExtent = {
				static_cast<uint32_t>(width), 
				static_cast<uint32_t>(height)
			};

			actualExtent.width = std::clamp(actualExtent.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width);
			actualExtent.height = std::clamp(actualExtent.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height);
			return actualExtent;
		}
	}

	bool checkDeviceExtensionSupport(VkPhysicalDevice device) {
		uint32_t extensionCount;
		vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr);
		std::vector<VkExtensionProperties> availableExtensions(extensionCount);
		vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data());
		std::set<std::string> requiredExtensions(deviceExtensions.begin(), deviceExtensions.end());
		for (const auto& extension : availableExtensions) {
			requiredExtensions.erase(extension.extensionName);
		}
		return requiredExtensions.empty();
	}
	
	// mainLoop函数里包含了一个循环，直到窗口被关闭，才会跳出这个循环
	void mainLoop() {
		// glfwWindowShouldClose是GLFW的函数，用来判断某个窗口是否被要求关闭
		while (!glfwWindowShouldClose(window)) {
			// glfwPollEvents是GLFW的函数，从操作系统取出窗口/输入事件，并分发该回调或内部状态
			glfwPollEvents();
			// 信号量用于指定GPU上操作的执行顺序，而栅栏用于保持CPU和GPU彼此同步，
			// 信号量常用交换链操作，因为发生在GPU；等待上一帧完成的操作，常用栅栏，因为需要主机等待，这样就不会一次绘制多个帧
			drawFrame();
		}
		
		// vkDeviceWaitIdle是用于等待设备空闲的函数
		vkDeviceWaitIdle(device);
	}

	void drawFrame() {
		// vkWaitForFences用于等待一个或多个fence对象的状态变化，通常是等待GPU完成某个任务或操作
		vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX);
		uint32_t imageIndex;

		// vkAcquireNextImageKHR用于从交换链（SwapChain）中获取下一个图像
		VkResult result = vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex);
		
		// VK_ERROR_OUT_OF_DATE_KHR 交换链与Surface不兼容，无法再用于渲染，通常在调整窗口大小后发生
		// VK_SUBOPTIMAL_KHR 交换链仍可用于成功呈现到表面，但表面属性不再完全匹配
		if (result == VK_ERROR_OUT_OF_DATE_KHR) {
			recreateSwapChain();
			return;
		}
		else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {
			throw std::runtime_error("failed to acquire swap chain image!");
		}

		updateUniformBuffer(currentFrame);

		// vkResetFences用于重置一个或多个fence函数，使其回到非信号状态
		vkResetFences(device, 1, &inFlightFences[currentFrame]);

		// vkResetCommandBuffer用于重置命令缓冲区（Command Buffer），使其重新变为初始状态
		vkResetCommandBuffer(commandBuffers[currentFrame], /*VkCommandBufferResetFlagBits*/ 0);

		recordCommandBuffer(commandBuffers[currentFrame], imageIndex);
		
		// VkSubmitInfo用于提交命令到图形队列或计算队列的结构体
		// sType 结构体标识
		// waitSemaphoreCount 等待信号量的数量
		// pWaitSemaphores 指向信号量数组的指针
		// pWaitDstStageMask 指向VkPipelineStageFlags数组的指针，指定在各个信号量被信号化时，哪些管线阶段需要等待
		// commandBufferCount 提交的命令缓冲区的数量
		// pCommandBuffers 指向命令缓冲区的数组的指针
		// signalSemaphoreCount 提交操作完成后将被信号化的信号量数量
		// pSignalSemaphores 提交操作完成后指向将被信号化的信号量数组的指针
		VkSubmitInfo submitInfo{};
		submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
		VkSemaphore waitSemaphores[] = { imageAvailableSemaphores[currentFrame] };
		// VkPipelineStageFlags用于描述管线阶段（Pipeline Stages）的一组标志位
		VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
		submitInfo.waitSemaphoreCount = 1;
		submitInfo.pWaitSemaphores = waitSemaphores;
		submitInfo.pWaitDstStageMask = waitStages;
		submitInfo.commandBufferCount = 1;
		submitInfo.pCommandBuffers = &commandBuffers[currentFrame];
		VkSemaphore signalSemaphores[] = { renderFinishedSemaphores[currentFrame] };
		submitInfo.signalSemaphoreCount = 1;
		submitInfo.pSignalSemaphores = signalSemaphores;
		
		// vkQueueSubmit用于将命令缓冲区提交到指定队列（Queue）执行的函数
		if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]) != VK_SUCCESS) {
			throw std::runtime_error("failed to submit draw command buffer!");
		}
		
		// VkPresentInfoKHR用于提交图像到交换链（swapchain）并进行呈现的结构体
		// sType结构体标识
		// waitSemaphoreCount 等待的信号量数量
		// pWaitSemaphores 指向信号量数组的指针
		// swapchainCount 交换链数量
		// pSwapchains 指向交换链数组的指针
		// pImageIndices 指向交换链图像索引数组的指针
		VkPresentInfoKHR presentInfo{};
		presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
		presentInfo.waitSemaphoreCount = 1;
		presentInfo.pWaitSemaphores = signalSemaphores;
		// VkSwapchainKHR用于标识交换链（swap chain）对象的类型，是图形渲染管线与显示设备之间的桥梁
		VkSwapchainKHR swapChains[] = {swapChain};
		presentInfo.swapchainCount = 1;
		presentInfo.pSwapchains = swapChains;
		presentInfo.pImageIndices = &imageIndex;
		// presentInfo.pResults = nullptr;
		// vkQueuePresentKHR用于提交图像到交换链并显示在屏幕上的函数
		result = vkQueuePresentKHR(presentQueue, &presentInfo);
		if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || framebufferResized) {
			framebufferResized = false;
			recreateSwapChain();
		}
		else if (result != VK_SUCCESS) {
			throw std::runtime_error("failed to present swap chain image!");
		}
		currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
	}

	void updateUniformBuffer(uint32_t currentImage) {
		
		// std::chrono::high_resolution_clock是时钟类型，表示当前系统能够提供的最高精度时钟
		static auto startTime = std::chrono::high_resolution_clock::now();
		auto currentTime = std::chrono::high_resolution_clock::now();
		// std::chrono::duration设置指定时间单位的时间间隔
		float time = std::chrono::duration<float, std::chrono::seconds::period>(currentTime - startTime).count();

		UniformBufferObject ubo{};
		
		// glm::rotate是GLM的库函数，用于执行旋转变换，将对象按指定角度绕给定轴进行旋转
		// glm::radians是GLM的库函数，将于将角度从度转换为弧度，将角度转换为弧度
		ubo.model = glm::rotate(glm::mat4(1.0f), time * glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f));
		// glm::lookAt是GLM的库函数，用于生成视图矩阵，用于定义观察（摄像机）的位置和方向，通常用于设置3D摄像机的视角
		ubo.view = glm::lookAt(glm::vec3(2.0f, 2.0f, 2.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f));
		
		// glm::perspective是GLM的库函数，用于生产透视投影矩阵，透视投影矩阵用于将三维场景投影到二维屏幕上，
		// 同时保持远近物体的透视关系，也就是远处的物体看起来更小，近处的物体看起来更大
		ubo.proj = glm::perspective(glm::radians(45.0f), swapChainExtent.width / (float)swapChainExtent.height, 0.1f, 10.0f);

		// GLM最初是为OpenGL设计的，其中裁剪坐标的Y坐标是反转的。最简单的补偿方法是反转投影矩阵中Y轴缩放因子的符号
		ubo.proj[1][1] *= -1;
		
		memcpy(uniformBuffersMapped[currentImage], &ubo, sizeof(ubo));
	}

	// checkValidationLayerSupport检查验证层支持 
	bool checkValidationLayerSupport() {
		uint32_t layerCount;
		// vkEnumerateInstanceLayerProperties用来检索支持的Vulkan层（Layer）的信息的函数
		vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
		std::vector<VkLayerProperties> availableLayers(layerCount);
		vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());

		std::cout << "available layers:\n";

		for (const auto& layer : availableLayers) {
			std::cout << '\t' << layer.layerName << '\n';
		}

		for (const char* layerName : validationLayers) {
			bool layerFound = false;
			for (const auto& layerProperties : availableLayers) {
				if (strcmp(layerName, layerProperties.layerName) == 0) {
					layerFound = true;
					break;
				}
			}
			if (!layerFound) {
				return false;
			}
		}
		return true;
	}

	// getRequiredExtensions获取请求的扩展
	std::vector<const char*> getRequiredExtensions() {
		uint32_t glfwExtensionCount = 0;
		const char** glfwExtensions;
		glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
		std::vector<const char*> extensions(glfwExtensions, glfwExtensions + glfwExtensionCount);
		if (enableValidationLayers) {
			extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
		}

		return extensions;
	}
	
	// Vulkan 中的几乎所有操作，从绘制到上传纹理，都需要将命令提交到队列，
	// 不同类型的队列源自不同的 队列家族，每个队列家族只允许一部分命令
	QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) {
		QueueFamilyIndices indices;
		uint32_t queueFamilyCount = 0;
		// vkGetPhysicalDeviceQueueFamilyProperties用于查询物理设备（GPU）支持的队列族（Queue Families）的信息
		vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);
		// VkQueueFamilyProperties用于描述一个队列族属性的结构体
		std::vector<VkQueueFamilyProperties> queueFamlies(queueFamilyCount);
		vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamlies.data());

	
		// 查看支持队列族
		std::cout << "available queues:\n";
		for (const auto& queueFamily : queueFamlies) {
			std::cout << '\t' << queueFamily.queueFlags << '\n';
		}

		int i = 0;
		for (const auto& queueFamily : queueFamlies) {
			// queueFlags位标志，表示此队列族支持的队列类型
			if (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
				indices.graphicsFamily = i;
			}
			
			VkBool32 presentSupport = false;
			// vkGetPhysicalDeviceSurfaceSupportKHR是检查某个物理设备的某个队列族是否支持向指定的Surface呈现图像的函数
			vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport);
			if (presentSupport) {
				indices.presentFamily = i;
			}

			if (indices.isComplete()) {
				break;
			}
			i++;
		}
		return indices;
	}

	// 为了在vkCreateInstance时也可以进行调试信息，需要在处理一下
	// 这里是使用pNext链式扩展指针实现
	void populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo) {
		createInfo = {};
		createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
		createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT |
			VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
			VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
		createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
			VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
			VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
		createInfo.pfnUserCallback = debugCallback;
	}

	static std::vector<char> readFile(const std::string& filename) {
		// std::ios::binary 以二进制格式打开文件
		// std::ios::ate 打开文件后，立即将文件指针移动到文件末尾
		std::ifstream file(filename, std::ios::ate | std::ios::binary);
		if (!file.is_open()) {
			throw std::runtime_error("failed to open file!");
		}
		// tellg 返回当前读指针的位置，单位是字节
		size_t fileSize = (size_t)file.tellg();
		std::vector<char> buffer(fileSize);
		// seekg 设置读指针的位置
		file.seekg(0);
		file.read(buffer.data(), fileSize);
		file.close();
		return buffer;
	}

	static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(
		// messageSeverity用于指定消息的严重性
		// VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT 诊断消息
		// VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT 信息性消息，例如创建资源
		// VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT 有关行为的消息不一定是错误，但很可能是应用程序中的错误
		// VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT 有关无效行为且可能导致崩溃的消息

		// messageType用于指定消息的类型
		// VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT 发生了一些与规格或性能无关的事件
		// VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT 发生了违反规范的事情或表明可能存在错误
		// VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT Vulkan 的潜在非最佳使用

		// pCallbackData是包含信息本身详细信息的结构体
		// pMessage 以空字符结尾的字符串形式的调试信息
		// pObjects 与信息相关的Vulkan对象句柄数组
		// objectCount 数组中的对象数量

		// pUserData包含在回调设置期间指定的指针，并允许将自己的数据传递给它
		VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, 
		VkDebugUtilsMessageTypeFlagsEXT messageType, 
		const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, 
		void* pUserData) {
		std::cerr << "validation layer: " << pCallbackData->pMessage << std::endl;

		// 回调返回一个布尔值，指示是否应中止触发验证层消息的Vulkan调用
		return VK_FALSE;
	}

	// cleanup函数进行完成资源的清理
	void cleanup() {
		cleanupSwapChain();

		for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
			vkDestroyBuffer(device, uniformBuffers[i], nullptr);
			vkFreeMemory(device, uniformBuffersMemory[i], nullptr);
		}
		// vkDestroyDescriptorPool用于销毁描述符池（VkDescriptorPool）的函数
		vkDestroyDescriptorPool(device, descriptorPool, nullptr);
		
		// vkDestroySampler用于销毁已创建的纹理采样对象的函数
		vkDestroySampler(device, textureSampler, nullptr);

		// vkDestroyImageView用于销毁图像视图的函数
		vkDestroyImageView(device, textureImageView, nullptr);
			
		// vkDestroyImage用于销毁图像（VkImage）对象的函数
		vkDestroyImage(device, textureImage, nullptr);
		vkFreeMemory(device, textureImageMemory, nullptr);

		// vkDestroyDescriptorSetLayout用于销毁描述符集布局的函数
		vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);

		// vkDestroyPipeline用于销毁图像和计算管线的函数
		vkDestroyPipeline(device, graphicsPipeline, nullptr);
		// vkDestroyPipelineLayout是用于销毁管线布局（Pipeline Layout）的函数
		vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
		// vkDestroyRenderPass是销毁渲染通道（Render Pass）对象的函数
		vkDestroyRenderPass(device, renderPass, nullptr);

		vkDestroyBuffer(device, indexBuffer, nullptr);
		vkFreeMemory(device, indexBufferMemory, nullptr);

		// vkDestroyBuffer用来销毁缓冲区（VkBuffer）对象的函数
		vkDestroyBuffer(device, vertexBuffer, nullptr);

		// vkFreeMemory用来释放设备内存的函数
		vkFreeMemory(device, vertexBufferMemory, nullptr);

		for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) {
			// vkDestroySemaphore是用于销毁信号量（VkSemaphore）的函数
			vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr);
			vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr);
			// vkDestroyFence是用于销毁栅栏（VkFence）对象的函数
			vkDestroyFence(device, inFlightFences[i], nullptr);
		}
		// vkDestroyCommandPool销毁命令池（VkCommandPool）的函数
		vkDestroyCommandPool(device, commandPool, nullptr);
		
		// 销毁物理设备
		vkDestroyDevice(device, nullptr);
		if (enableValidationLayers) {
			DestroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr);
		}
		// vkDestroySurfaceKHR是用来销毁用vkCreateSurfaceKHR创建的表面（VkSurfaceKHR）的函数
		vkDestroySurfaceKHR(instance, surface, nullptr);
		// vkDestroyInstance是Vulkan用来销毁实例（VkInstance）的函数
		vkDestroyInstance(instance, nullptr);
		// glfwDestroyWindow是GLFW用来销毁窗口的函数
		glfwDestroyWindow(window);
		// glfwTerminate是GLFW库的清理函数，用于在程序退出前释放GLFW内部申请的所有资源
		glfwTerminate();
	}
};


int main() {
	HelloTriangleApplication app;
	try {
		app.run();
	}
	catch (const std::exception& e) {
		std::cerr << e.what() << std::endl;
		return EXIT_FAILURE;
	}

	return EXIT_SUCCESS;
}
